Skip to content

A simple introduction to webpack principle

About 1279 wordsAbout 4 min

nodewebpack

2021-03-21

webpack

Preface

We know that webpack is one of the mainstream module packaging tools in front-end engineering and is used in various front-end engineering projects.

Although most projects will use webpack to a greater or lesser extent, it may be for most front-end developers. Maybe it's just changing the configuration of webpack, or even never touching the related files of webpack, More or less unfamiliar with the configuration and functionality of webpack.

There are also various scaffolding based on webpack encapsulation, such as vue-cli, create-react-app, umi.js, etc. A variety of out-of-the-box features are provided, which makes webpack seem to be getting further and further away from us.

But when one of our projects faces problems that we have to go deep into webpack to solve, or during an interview, we are asked about webpack related questions. It's difficult to solve or answer.

So we need to at least have a basic understanding of webpack, understand its principles, how to write loader, plugin, etc.

What is webpack

Quote webpack official website:

At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, which are static assets to serve your content from.

Essentially, webpack is a static module bundler for modern JavaScript applications. When webpack handles an application, it recursively builds a dependency graph, This contains each module that the application needs and then package all of these modules into one or more bundles.

In terms of function, the function of webpack is to package and integrate files of different modules together, and ensure that the references between them are correct and executed in an orderly manner. This allows us to split files from the perspective of modules when doing project architecture, and then hand them over to webpack for packaging and integration.

The files in a project include not only html files, CSS files, JavaScript files, picture resources, and Vue-specific .vue files. Typescript's .ts file, etc., as well as the code in the project, also needs to be compressed and obfuscated, browser compatibility, etc., Starting a local development server, hot update replacement of modules, etc., can be implemented one by one through various mechanisms provided by webpack.

For webpack, it can only recognize JavaScript files itself, and for other resources, it can be implemented through the Loader feature provided by webpack Identify. Through Loader, other types of resource files can be converted into valid modules that webpack can handle.

For code obfuscation, local development server, and module hot updates, functional extensions can be achieved through the Plugin feature provided by webpack.

Module packaging principle

In webpack, there are four basic and core concepts:

  • Entries (entry)
  • Output
  • Loader (Loader)
  • Plugin (Plugin)

Entry

Indicates which module webpack should use as the beginning of building its internal dependency graph.

Output

Tell webpack where to output the bundles it creates and how to name these files

Loader

webpack itself can only understand JavaScript files and json files, and loader can convert other types of resource files into valid modules that webpack can handle.

Essentially, webpack loader converts all types of files into modules that can be directly referenced by the application's dependency graph (and the final bundle).

Plugin

Used to perform a wider range of tasks. The scope of plug-ins includes, from packaging optimization and compression, to redefining variables in the environment. The plug-in interface is extremely powerful and can be used to handle a variety of tasks.

modules

In modular programming, developers decompose programs into discrete chunks of functionality and call them modules.

For webpack, any file can be a module.

###Module packaging operation principle

Before talking about the ** module packaging operation principle** of webpack, let’s take a look at how we use webpack. Generally speaking, we localize webpack by writing a configuration file webpack.config.js. The rough configuration is as follows:

module.exports = {
  // Declare the module's entry file
  entry: './src/entry.js',
  output: {
    path: path.resolve(__dirname, 'dist'), // Output directory
    filename: 'bundle.js', // File name
  },
  module: {
    rules: [
      //Configuration Use babel-loader to convert .js resources
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
      // ...more loader
    ],
  },
  // Plug-in configuration
  plugins: [
    new EslintWebpackPlugin(),
    new webpack.NoEmitOnErrorsPlugin(),
    // ...more plugin
  ],
  // ...more config
}

After webpack has read the configuration file, the running process is roughly as follows:

  1. Read the configuration parameters of webpack;
  2. Start webpack, create compiler object, and start parsing the project;
  3. Start parsing from the entry file entry, and find the ** dependency module** it imported, recursively traverse the analysis, and form the ** dependency tree**;
  4. Use the corresponding Loader to convert the dependency module files for different file types resources, and finally convert them into valid modules of webpack;
  5. During the compilation process, webpack throws some hooks through the publish subscription mode, and webpack's Plugin is listened to various hooks, Execute plug-in tasks, expand the functions of webpack, and interfere with the output results.
  6. According to the output configuration output, the packaged built resource file is output.

The compiler object is a global singleton that controls the entire webpack construction process.

During the build process, a context object compilation of the current build will also be generated, which contains all the information of the current build. During each hot update or rebuild, compiler will generate a new compilation object, responsible for the current build process.

The dependencies between each module depend on the AST syntax tree. After each module file is parsed through Loader, The AST syntax tree of module code will be generated through the acorn library. Through the syntax tree, you can analyze whether this module has dependent modules. Then continue to loop through the compilation and parsing of the next module.

Finally, the bundle file built by webpack is a IIFE execution function.

// Minimize package output file under webpack5
;(() => {
  // webpack module file content
  var __webpack_modules__ = {
    'entry.js': (modules) => {
      /* ... */
    },
    'other.js': (__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
      /* ... */
    },
  }

  // Module Cache
  var __webpack_module_cache__ = {}

  // The require function
  function __webpack_require__(moduleId) {
    // Check if module is in cache
    var cachedModule = __webpack_module_cache__[moduleId]
    if (cachedModule !== undefined) {
      return cachedModule.exports
    }
    // Create a new module (and put it into the cache)
    var module = (__webpack_module_cache__[moduleId] = {
      // no module.id needed
      // no module.loaded needed
      exports: {},
    })

    // Execute the module function
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__)

    // Return the exports of the module
    return module.exports
  }

  // startup
  // Load entry module and return exports
  // This entry module can't be inlined because the eval devtool is used.
  var __webpack_exports__ = __webpack_require__('entry.js')
})()

In the package demo above, there are only three variables and one function method in the entire immediate execution function. __webpack_modules__ stores the JS content of each compiled file module. __webpack_module_cache__ is used for module cache. __webpack_require__ is a set of dependency introduction functions implemented internally by Webpack. The last sentence is the starting point of the code running, starting from the entry file and starting the entire project.

__webpack_require__ module introduces functions. When we develop modularly, we usually use ES Module or CommonJS specification to export/introduce dependent modules. When webpack is packaged and compiled, it will be replaced with its own __webpack_require__ to implement the module introduction and export, thereby implementing the module caching mechanism and smoothing out some differences between different module specifications.