How to use dllplugin package to extract public dependencies and the problems encountered in the micro front end (Qiankun) project

Posted by Dark-Hawk on Thu, 20 Jan 2022 14:21:29 +0100

preface

For large projects, it is often necessary to extract the dependencies of common parts to avoid repeated loading of dependencies. When extracting public plug-ins, we can consider using externals [Webpack's externals. note].
However, the premise of externals is that all dependencies must have a cdn or find its corresponding JS file, such as jQuery Min.js, which means that these dependent plug-ins must support umd format.
The dll plug-in can help us directly install the installed dependencies on the node_module, combined with the add asset html webpack plugin plug-in, helps us insert the generated packaged js file into html (we don't need to manually write script s like externals)

1. Install required plug-ins

Three plug-ins

  • Webpack cli / / general items have been installed
  • Add asset html webpack plugin / / used to dynamically insert dependencies into html through script tags
  • Clean webpack plugin / / empty the folder
npm install webpack-cli@^3.2.3 add-asset-html-webpack-plugin@^3.1.3 clean-webpack-plugin@^1.0.1 --dev

2. Write dll configuration related files

Under the root directory (the path can be determined by yourself), create a new webpack dll. Conf.js file

// webpack.dll.conf.js

// Introduce dependency
const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // For emptying files
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // For compressing code

// Specific configuration
module.exports = {
  mode: 'production', // Tell webpack that the current environment is a production environment
  // Entry dll custom module name: [dependency 1, dependency 2,...] 
  entry: {
    vue: ['vue', 'vue-router', 'vuex'], // Package vue, vue router and vuex dependencies into a dll module called vue
    elementui: ['element-ui'],
    vendor: ['axios']
  },
  // Export
  output: {
    filename: 'dll.[name].js', // Where [name] is the name of the dll module in the entry, so filename is dll vue. js
    path: path.resolve(__dirname, './dll/js'), // Output the packaged dependent files to the dll/js folder
    library: '[name]_library'// The exposed global variable name is used to map the manifest 
  },
  plugins: [
    // When repackaging, clear the previously packaged dll files
    new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, './dll/**/*')] // **For folders, * for files
    }),
    // Generate manifest JSON describes what the dynamic link library contains
    new webpack.DllPlugin({
      // Function name of exposed dll; And output are required here The value of library is consistent
      // Output manifest name value in JSON
      name: '[name]_library',
      context: __dirname, // It needs to be consistent with this in the main configuration of the project
      // Path specifies manifest The output path of the JSON file
      path: path.resolve(__dirname, './dll/[name]-manifest.json'),  // DllReferencePlugin uses this json file as a mapping dependency. (this file will tell us which files have been extracted and packaged)
    }),
    new BundleAnalyzerPlugin(),// compress
  ]
}

3. Package build dll

In package Add the following command to JSON

"scripts": {
    "build:dll": "webpack --config webpack.dll.config.js"
}

After running this command, the configuration of dll in step 2 will be executed to generate dll

4. Ignore compiled files in project main configuration

In order to save the compilation time, we need to tell webpack that the public library dependency has been compiled, so as to reduce the compilation time of webpack to the public library. Locate Vue. In the project root directory config. JS (New if not), the configuration is as follows:

//  vue.config.js
const webpack = require("webpack");

module.exports = {
      ...
      configureWebpack: {
         ['vue', 'elementui', 'vendor'].map(item => 
            config.plugins.push(
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require(path.resolve(__dirname, `./public/dll/${item}-manifest.json`))
              }),
          )
      );
     }
 }

5,index. Reference the generated DLL in HTML JS file

After the above configuration, the public library is extracted and the compilation speed is fast. However, if the generated dll file is not referenced, the project dependency has not been introduced and an error will be reported.
You can open public / index HTML, insert script tag manually.

<script src="./dll/js/vendor.dll.js"></script>

The extraction of this public library is completed, but I always feel that the last manual script insertion is not elegant. Here is how to automatically import the generated dll file.
Open Vue config. JS under the configureWebpack plugins configuration, continue to configure add asset HTML webpack plugin based on the configureWebpack in step 4

// vue.config.js 
// The required dependencies have been introduced
const path = require('path')
const webpack = require('webpack')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')

module.exports = {
      ...
      configureWebpack: {
         ['vue', 'elementui', 'vendor'].map(item => 
            config.plugins.push(
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require(path.resolve(__dirname, `./public/dll/${item}-manifest.json`))
              }),
          )
      );
      config.plugins.push(
        new AddAssetHtmlPlugin({
             // Referenced DLL JS file location
             filepath: path.resolve(__dirname, './public/dll/js/*.js'),
             // dll reference path the location of the dll static resource reference
             publicPath: './dll/js/*.js'),
             // dll final output directory after packaging, the specific file location under dist
             outputPath: './dll/js/*.js'),
             includeSourcemap: false
           })
      )
     }
 }

After configuring step 5, restart the project, refresh the browser page, and you will see the three js references we just set.

6. Child applications use these common dependencies

Taking the micro front end as the project background, the five steps of the appeal are configured in the main application, while in the sub application (the application that needs to use public dependencies), our configuration is relatively much simpler.
Just add the configuration in step 4 to the webpack configuration of the corresponding sub application, tell the webpack to ignore these dependencies and map them according to the manifest.

//  vue.config.js

module.exports = {
      ...
      configureWebpack: {
         ['vue', 'elementui', 'vendor'].map(item => 
            config.plugins.push(
            new webpack.DllReferencePlugin({
                context: __dirname,
                // Be careful not to write the path wrong
                manifest: require(path.resolve(__dirname, `../../main/public/dll/${item}-manifest.json`))
              }),
          )
      );
     }
 }

7. Introduction of on-demand dependency (depending on the situation)

Taking Ant Design Vue as an example, general component libraries depend on the following as needed:

import Button from 'ant-design-vue/lib/button';
import 'ant-design-vue/dist/antd.css'; //css style can be introduced directly

It was originally required to import on-demand, so we are not suitable to directly package the entire Ant Design Vue into the DLL module, so webpack dll. The entry in conf.js should be modified to:

// Entry dll module name: [dependency 1, dependency 2,,...] 
entry: {
    vendor: ['ant-design-vue/lib/button']
},

Disadvantages of dllplugin:

Because using public dependencies means that all applications that use public dependencies must use dependencies of the same version. In fact, I don't think it's a disadvantage. It's mandatory to standardize the dependent versions. After all, when there are many projects, the dependent versions should be unified. Therefore, when developing sub applications independently, when installing public dependencies, the version numbers must be the same. For example, if the main application is 5.1.0 and the sub application is 4.1.0, the dependent sub application cannot reference the public dependency of the main application and report an error because of the different reference methods of different versions of ecarts, so that the sub application cannot be started.

Problems encountered in the process:

1. When using public dependencies, sub applications sometimes report errors inexplicably, although I don't know what's going on.
For example, if you use highcharts (all references to highcharts are the same), application a is normal, but application b will report strange errors, while application c is more outrageous and cannot directly start the sub application...
The dll dependencies of these three references are consistent:

Error report of application b:

Error report of application c:

After many repeated attempts, by means of search and elimination, it is found that application b only modifies the order of dll modules in step 6 of the appeal. Simply move highcharts to the first place, and application b will be normal! (what the hell!!!)

However, application c also tries to change the order, but there is still no way to solve it. After removing highcharts, application c is normal...
ps: is application c tied to highcharts? Finally, the application c has no choice but to rely on the dll of highcharts.

2. Referring to the antd component library, there is a problem with the select all function of the table. In general, the use of page tables is normal, but the tables are in a pop-up window. Click Select all to print data. That is to say, there is indeed select all, but the class name of checked is not added. It seems that there is no select all. Moreover, I removed the reference of the sub application's antd to the dll and used the sub application node instead_ There is no problem with antd in module, so this problem is really related to the packaging of dll module. (the antd of the main application is 1.7.5 and that of the sub application is 1.7.2)

In order to confirm whether it is related to the version of antd, I upgraded the antd of the sub application to 1.7.5, and then used the dependency of 1.7.5. The result is normal (does it have anything to do with version 1.7.5?). But later, I changed the sub application back to dll dependency, changed the version of the antd component library of the main application to version 1.7.2, and repackaged the dll (I would restart the sub application service every time I repackaged the dll), and the problem was solved. So it's hard to say whether the problem is related to version 1.7.5 or not.

Problem summary:

  1. If you encounter problems related to resource loading, you can try to adjust the insertion order of dll modules in sub applications, that is, the order in the array in step 6 above.
  2. If there are functional problems with some application dependencies, try to keep the version consistent with the sub application development version, and then repackage the dll dependencies.

Summary:

externals and DLLs are very necessary for large projects with multiple applications, because repeated dependencies are extracted, which can greatly reduce the volume after packaging and optimize the loading of the project. At least in the micro front-end project developed by me, after testing, the volume is reduced by half. However, for a single application project, it may have little effect, reducing the packaging time at most, but the volume may not change much. After all, there will still be dependencies, and the size is difficult to change. If there are dependencies for reuse, you can consider using these two methods to optimize and reduce the package volume.

When packaging DLLs, remember that you don't have to package DLLs every time, because most of the modules packaged by DLLs are third-party modules that remain unchanged. Generally, you only need to package them once and store them in a fixed location (for example, under the public of the main application) to submit the code together. It can also reduce the packaging time of dependencies during run build, The dll needs to be repackaged every time the dll dependency is modified. After repackaging the dll, remember to restart the service for the sub application. If the dll dependency is added or deleted, remember to add or delete the dll module of the sub application.

Topics: Javascript Vue Webpack dll