In this chapter, we will take a look at how to optimize Webpack.

Git address: github.com/jxs7754/dem…

1. Optimization of construction speed

  • Advanced versions of Node and Webpack
  • Enable multiple processes to speed up parsing and compression
  • Subcontract, separate base package
  • Use caching to speed up secondary builds
  • Reduce file search scope

Speed analysis: use speed-measure-webpack-plugin

const SpeedMeasureWebpackPlugin = reqire('speed-measure-webpack-plugin');
const smp = new SeedMeasureWebpackPlugin();
const webpackCofig = smp.wrap({
  plugins:[
    // MyPlugin(),
  ]
})
Copy the code

You can analyze the total time of packaging and view the time of each loader and plugins.

1.1 Use older versions of Node and Webapck

  • V8 engine upgrade optimization
  • Webpack4 uses the faster MD4 hash algorithm by default
  • Webpacks AST can be passed directly from the Loader to the AST, reducing parsing time
  • Use string methods instead of regular expressions

1.2 Enabling multiple Processes

thread-loader

{
  module:{
    rules: [
      {
        test: '/.js$/',
        use: [
          {
            loader: 'thread-loader', options:{workers: 3,}},'babel-loader'}]}}Copy the code

HappyPack(no longer maintained by author)

const HappyPack = require('happypack');

exports.module = {
  rules: [{test: /.js$/.// 1) replace your original list of loaders with "happypack/loader":
      // loaders: [ 'babel-loader?presets[]=es2015' ],
      use: 'happypack/loader'.include: [ / *... * /].exclude: [ / *... * /]]}}; exports.plugins = [// 2) create the plugin:
  new HappyPack({
    // 3) re-add the loaders you replaced above in #1:
    loaders: [ 'babel-loader? presets[]=es2015']})];Copy the code

Multithreaded compression

// terser-webpack-plugin module.exports = { optimization: { minimizer: { new TerserPlugin({ parallel: // Uglifyjs-webpack-plugin // uglifyjs-webpack-pluginCopy the code

1.3 the subcontract

Set Externals, and run the html-webpack-externals-plugin command to pass the basic packet (vue vue-router) through the CDN without inserting it into the packet.

new HtmlWebpackExternalsPlugin({
  externals: [
    {
      module: 'react',
      entry: 'https://xxx/react.min.js',
      global: 'React',
    },
    {
      module: 'react-dom',
      entry: 'https://xxx/react-dom.min.js',
      global: 'ReactDOM',}]}),Copy the code

If there is no CDN, the DllPlugin can be precompiled for subcontracting, and the DllReferencePlugin refers to manifest.json

Module. exports = {mode: exports'production',
  entry: {
    vue: ['vue/dist/vue.esm.js'.'vue-router'.'vuex'],
    axios: ['axios'.'qs'],
    // ui: ['element-ui'],
  },
  output: {
    filename: '[name]_[chunkhash:8].dll.js',
    path: path.join(__dirname, 'build'),
    library: '[name]',
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.DllPlugin({
      name: '[name]_[hash]',
      path: path.join(__dirname, 'build/[name].json'),})]}; Module. exports = {plugins: [...['vue'.'axios'].map((item) => new webpack.DllReferencePlugin({
      context: path.join(__dirname, './build'),
      manifest: require(`./build/${item}.json`),
    })),
  ]    
}
Copy the code

1.4 the cache

Caching is used to speed up builds during secondary builds

Babel-loader enables caching

{
    loader: 'babel-loader',
    options:{
      cacheDirectory: true}}Copy the code

Terser-webpack-plugin enables caching

{optimization: {minimizer: {new TerserPlugin({// multi-thread parallel: 4, // cache:true,})}}}Copy the code

Hard – source – webpack – plugin or cache – loader

1.5 Reduce the file search scope

Optimizing loader Configuration

Because Loader’s file conversion operation is time-consuming, it is necessary to keep as few files as possible to be processed by Loader. You can use test, include, and exclude to match the file to which the Loader applies the rule.

Use reasonable aliases

In practical projects, some huge third-party modules are often relied on. Take Vue library as an example, the released Vue library contains multiple sets of code, while Vue.Runtime.esm.js only contains runtime code. If you don’t use the template option you can use this to reduce the packing volume.

module.exports = {
  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.runtime.esm.js',}}}Copy the code

Optimize the resolve.modules configuration

Resolve. Modules defaults to [‘node_modules’], which means to find the module in the node_modules directory of the current directory. If not, go to the upper directory.. /node_modules, then go to.. /.. /node_modules, and so on. When installed third-party modules are placed in the node_modules directory of the project root directory, there is no need to follow the default way of searching layer by layer. You can specify the absolute path to store third-party modules to reduce searching.

module.exports = {
  resolve: {
    modules: [path.resolve( __dirname,'node modules')]}}Copy the code

Optimize the resolve.mainfields configuration

There is a package.json file that describes the properties of the module in installed third-party modules. Multiple field description entry files can exist because some modules can be used in multiple environments at the same time, requiring different code for different runtime environments. Segmentfault.com/a/119000001…

Optimize the resolve.extensions configuration

When an import statement does not have a file suffix, Webpack automatically applies the suffix to try to ask if the file exists. The longer the list is, or the later the correct suffix is, the more attempts will be made, so the configuration of the resolve.extensions will also affect the performance of the build.

  • Keep the suffix list as small as possible
  • File suffixes with high frequency are given priority
  • Source code to write import statements, as far as possible with suffixes
 {
    extensions: ['.js'],},Copy the code

2. Build volume optimization

  • Extract common code, split code, load on demand, lazy load
  • tree-shaking
  • scope-hoisting
  • Delete unnecessary CSS
  • Dynamic polyfill
  • Code compression, enable Gzip compression

Volume analysis Webpack-bundle-Analyzer

You can analyze the size of dependent third-party modules and the code size of components within the business

const BoundAnalysisPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
  plugins: [
    new BoundAnalysisPlugin(),
  ]
}
Copy the code

2.1 Extract common code, split code, load on demand, lazy load

// The component loads import {Button} from on demand'element-ui'; // modules load import {on demandcloneDeep} from 'lodash-es'; // Vue route lazy loading const Foo = () => import(/* webpackChunkName:"group-foo"* /'./Foo.vue')
Copy the code
optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      minRemainingSize: 0,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      automaticNameDelimiter: '~',
      automaticNameMaxLength: 30,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true}}}}Copy the code

2.2 the tree – shaking

A module may have multiple methods, and as long as one of them is used, the whole file will be sent to the bundle. Tree shaking is only putting used methods into the bundle, and unused methods will be erased during the Uglify phase. Matters needing attention:

  • Mode: production Default Babel set modules:false,
  • ES6 syntax must be used

2.3 Deleting unnecessary CSS

Use purgecss-Webpack-Plugin with mini-CSS-extract-Plugin

const config = {
  module:{
      rules: [
        {
          test: '/.scss$/',
          use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'sass-loader',
          {
            loader: 'postcss-loader', options: {plugins: () => [// automatic extension CSS require('autoprefixer')(),
              ],
            },
          },
        }
      ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: getAssetPath(
        `css/[name]_[contenthash:8]'}.css`, ), }), new PurgecssPlugin({ paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }), }), ] }Copy the code

2.4 dynamic polyfill

plan advantages disadvantages
babel-polyfill Instead of Volume is too big
@babel/plugin-transform-runtime Polyfill only uses methods and classes that are small in size Cannot polyfill the method on the prototype
polyfill-service Only the polyfills required by the customer are returned Weird Browsers in China

2.5 the Scope – Hoisting

ModuleConcatenationPlugin webpack4 now has support mode is not equal to none

2.6 Image compression, code compression, can also enable Gzip compression

Use image-webpack-loader for image compression

3. Load optimization

3.1 preload

Use @vue/preload-webpack-plugin for code preloading

const config = {
  plugins: [
    new PreloadPlugin({
      rel: 'preload',
      include: 'initial',
      fileBlacklist: [/\.map$/, /hot-update\.js$/],
    }),
    new PreloadPlugin({
      rel: 'prefetch',
      include: 'asyncChunks',]}})Copy the code

3.2 Use file fingerprint and browser cache

  • Hash: Relates to the construction of the entire project. The Hash value for the entire project will change whenever the project file is modified
  • Chunkhash: Related to chunks packed by Webpack, different entries will generate different Chunkhash values
  • Contenthash: Defines hash based on the file content. Contenthash does not change if the file content is not changed
// js
{
  output: {
    filename: '[name]_[chunkhash:8].js'  
  }    
}
// css
// MiniCssExtractPlugin
{
  plugins:[
    new MiniCssExtractPlugin({
      filename: '[name]_[contenthash:8].css'})]} // image // file-loader is usedhash(where thehashMd5) {module:{rules:[{test: /\.(png|svg|jpg|gif)$/,
        use: [{
          loader: 'file-loader ', options: {name: 'img/[name][hash:8].[ext] '}}]}}Copy the code

. To be continued