Author: Sun Mengge

Webpack has become an indispensable engineering tool for developing Web applications, and the plug-in system, as the backbone function of Webpack, is a hurdle that we can’t get around in our in-depth study of Webpack. From a plug-in development perspective, you can have more fine-grained control over webPack’s work, doing things you can’t do by modifying configuration files.

Applets & Webpack

Recently, the company team used Webpack to do engineering for wechat small program project. Js file of each page is an independent entry of Webpack. After compilation and packaging, each entry corresponds to generate an output JS file. Modules that reference the node_modules folder are also packed in, so you don’t need to execute the appletsBuild the NPMThis process. But it causes the problem of repeated module packaging:

As shown in the figure above, module A is referenced by page 1 and page 2, and finally module A is repeatedly packaged into the output file of the two pages, resulting in large volume of small program packaging.

This problem was resolved by modifying the WebPack configuration to extract the common modules. After all, I skipped the process of “building NPM,” so I decided to develop a WebPack plug-in that mimics this process of applets.

What to do?

Let’s take a look at what the applet does in the “build NPM” step:

  1. First, node_modules doesn’t participate in compiling, uploading, and packaging, so small applications that want to use NPM packages must go through the “build NPM” process. In the outermost node_modules of package.json declared by the developer in each miniprogramRoot, a miniprogram_npm directory is generated, which holds the build packed NPM package. This is the NPM package that the applets actually use.
  2. There are two types of build packages: the miniprogram NPM package directly copies all files in the build file generation directory to miniprogram_npm. Other NPM packages go through the dependency analysis and packaging process (similar to WebPack), starting with the entry JS file.
  3. The process of finding an NPM package is similar to the implementation of NPM, starting from the directory where the files that depend on the NPM package reside and working out until you find a usable NPM package or applets root directory.

The directory structure after the build is roughly as follows:

| - node_modules | - miniprogram_npm | | - testComp / / applet NPM package | | | - index. Js | | | - index. Json | | | - index. WXSS | | | - index. WXML | | - testa / / other NPM package | | - index. The js / / documents after package | | - miniprogram_npm | | - testb | | -- index. | js / / after the packaging file |--index.js.map |--pages |--app.js |--app.wxss |--app.json |--project.config.jsCopy the code

Copy the modules from node_modules to the miniprogram_npm directory at the same level. Then change all references to node_modules to miniprogram_npm, such as require(‘node_modules/ ABC ‘) to require(‘miniprogram_npm/ ABC ‘).

All we need to do is duplicate the steps above with the Webpack plugin:

  1. Walk through all the Modules parsed by WebPack to find all the public Modules (node_modules directory, and the directory passed to the plug-in as an argument)
  2. The public Modules are unbound from all Chunks that are referenced
  3. Create new Chunks for each common Module, associate a Module with each Chunk, and insert Chunks into the list
  4. Generate Assets phase, modify the file source code, according to the dependencies of this module and target module reasons, insert require statement

How to do?

1. Get a general idea of official documentation

In general, you don’t need to look at it in detail before you start developing, because most of the knowledge is not documented. May have a look this: webpack.js.org/contribute/… As an introduction.

Tapable, know which type of hook to use, the principle does not need to be studied.

(Be sure to see webpack English official website, Chinese official website updated later!)

2. Use breakpoint debugging

Since we are programming on the node side, we can break points in webstorm, vscode and other editors and start debug mode

The data structure of each variable is clear at a glance.

3. Understand the structure of common webPack variables

For example, Compiler, Compilation, Module, Chunk, etc. The official documentation does not explain this part, so we still need to use breakpoint + source code.

  • Staring at breakpoints: You may need to stare at the data structure for a long time, many times, while in breakpoint mode
  • Look at the source code: look at the common method of class, TS file (some TS types relative to the source code less things, should be the official did not have time to update)

4. When adding, deleting, or modifying internal variables, call the encapsulated function first

For example, to unbind module and chunk, if module._chunk.delete(chunk) is executed, only the dependence of module on chunk is removed. The bidirectional dependence between module and chunk means that the dependence of chunk on Module is reversed.

But it doesn’t have to be that troublesome. Using the source code, we found that we can call a method provided by the Module class directly:

removeChunk(chunk) {
    if (this._chunks.delete(chunk)) {
        chunk.removeModule(this);
        return true;
    }
    return false;
}
Copy the code

The bidirectional dependency between Module and Chunk is removed.

5. Get it right: hooks

Compiler and Compilation offer all kinds of hook function, can refer to this document: webpack.js.org/api/compile… . The functions in the list are arranged in the order in which they are executed.

Compiler: I understand it as an instance of Webpack, the globally unique Compilation: the Compilation is performed once in run mode, and the Compilation is performed once for every file change in Watch mode

Hook functions are explained in the documentation, but I still don’t know where to start, how to do? At this time can find a more mature plug-in, reference its source code.

For example, the function I want to do is very similar to the function of SplitChunkPlugin, so I went to Github to search, but did not find… It is then discovered that it has been built into Webpack and found in the webPack source code. Then it is time to emulate it by referring to its hooks pointcut. This is the most beginner friendly approach, especially if you are confused by hooks documentation.

6. Modify WebPack Config

What if the plugin needs to modify the WebPack configuration file to implement the functionality? You can actually change it inside the plugin:

compiler.hooks.environment.tap('HelloWorldPlugin'.() = > {
    // To modify webpack Config, you must extract runtime.js
    compiler.options.optimization.runtimeChunk = {
      	name: 'runtime'}});Copy the code

Access and modify the compiler. Options object in the appropriate hooks.

Or, instead of doing so, manually modify the configuration file. Either way, you can choose.

results

Plug-in reference:

new HelloWorldPlugin({
    libPaths: ['src/utils']}),Copy the code

Packaging results after introducing the plug-in:

| - mp_node_modules / / similar small program miniprogram_npm directory | - pages | - utils | - components | -- app. Js | -- app. WXSS | -- app. Json |--runtime.js |--sitemap.jsonCopy the code

Insert the require statement at the head of the file according to dependencies:

Applets package defect

Subpackages will be packaged according to the SubPackages configuration path. Directories outside the subPackages configuration path will be packaged into the app (main package) ———— applet document/using the subpackages/packaging principle

Due to this principle, all node_modules are packaged into the main package. Therefore, for larger applications, as the project scale expands, the main package may exceed the upper limit of 2M at any time, and the service needs to be split into several small programs to solve the problem.

However, from another point of view, it may be that the original intention of the small program is “small”, and it may be the official intention of wechat.