Skip to content

Webpack 优化与 DLL ​

分析包内容 ​

bash
yarn add -D webpack-bundle-analyzer
yarn add -D webpack-bundle-analyzer
js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
  plugins: [new BundleAnalyzerPlugin()],
}
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
module.exports = {
  plugins: [new BundleAnalyzerPlugin()],
}

noParse ​

不去解析某个包(如 jquery 没有引用其他模块),减少解析时间:

js
module: {
  noParse: /jquery/,
}
module: {
  noParse: /jquery/,
}

exclude & include ​

js
module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      include: path.resolve('src'),
    },
  ],
}
module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      include: path.resolve('src'),
    },
  ],
}

IgnorePlugin(忽略不必要的引入) ​

忽略包内部引入的所有文件,如 moment 会默认加载所有 locale 文件:

js
const webpack = require('webpack')
module.exports = {
  plugins: [
    new webpack.IgnorePlugin(/\.\/locale/, /moment/),
  ],
}
// 需要时手动引入所需语言包:
// import 'moment/locale/zh-cn'
const webpack = require('webpack')
module.exports = {
  plugins: [
    new webpack.IgnorePlugin(/\.\/locale/, /moment/),
  ],
}
// 需要时手动引入所需语言包:
// import 'moment/locale/zh-cn'

DllPlugin(动态链接库) ​

将不常变动的第三方库(react、axios 等)提前打包,减少每次构建时间。

1. 创建 dll 打包配置 webpack.dll.js ​

js
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: {
    react: ['react', 'react-dom'],
    others: ['axios', 'js-cookie'],
  },
  output: {
    filename: '_dll_[name].js',
    path: path.resolve(__dirname, 'dist'),
    library: '_dll_[name]',
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.DllPlugin({
      name: '_dll_[name]',
      path: path.resolve(__dirname, 'dist', 'manifest.json'),
    }),
  ],
}
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
  entry: {
    react: ['react', 'react-dom'],
    others: ['axios', 'js-cookie'],
  },
  output: {
    filename: '_dll_[name].js',
    path: path.resolve(__dirname, 'dist'),
    library: '_dll_[name]',
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.DllPlugin({
      name: '_dll_[name]',
      path: path.resolve(__dirname, 'dist', 'manifest.json'),
    }),
  ],
}

运行 npx webpack --config webpack.dll.js 打包一次,然后在 HTML 中引入:

html
<script src="/_dll_react.js"></script>
<script src="/_dll_react.js"></script>

2. 修改主配置引用 ​

js
module.exports = {
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, 'dist', 'manifest.json'),
    }),
  ],
}
module.exports = {
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, 'dist', 'manifest.json'),
    }),
  ],
}

HappyPack(多线程打包) ​

项目较大时使用,小项目反而更慢:

bash
yarn add happypack
yarn add happypack
js
const Happypack = require('happypack')
module.exports = {
  module: {
    rules: [
      { test: /\.js$/, use: 'Happypack/loader?id=js' },
    ],
  },
  plugins: [
    new Happypack({
      id: 'js',
      use: [
        { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } },
      ],
    }),
  ],
}
const Happypack = require('happypack')
module.exports = {
  module: {
    rules: [
      { test: /\.js$/, use: 'Happypack/loader?id=js' },
    ],
  },
  plugins: [
    new Happypack({
      id: 'js',
      use: [
        { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } },
      ],
    }),
  ],
}

Tree Shaking(摇晃树) ​

在生产环境下,import 引入的代码会自动去除没用的代码(即 tree-shaking)。

Scope Hoisting(作用域提升) ​

Webpack 3+ 自动简化代码,将声明过于简洁的变量(如单个数字)直接省略。

TerserPlugin(压缩 JS) ​

bash
npm i terser-webpack-plugin -D
npm i terser-webpack-plugin -D
js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          format: { comments: false },
          compress: { drop_console: true },
        },
        extractComments: false,
      }),
    ],
  },
}
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          format: { comments: false },
          compress: { drop_console: true },
        },
        extractComments: false,
      }),
    ],
  },
}

CssMinimizerPlugin(优化 CSS) ​

bash
npm i css-minimizer-webpack-plugin -D
npm i css-minimizer-webpack-plugin -D
js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [
      new CssMinimizerPlugin({
        minimizerOptions: {
          preset: ['advanced', { zindex: false }],
        },
      }),
    ],
  },
}
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [
      new CssMinimizerPlugin({
        minimizerOptions: {
          preset: ['advanced', { zindex: false }],
        },
      }),
    ],
  },
}

CSS 压缩与 JS 优化配合 ​

js
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true }),
      new OptimizeCssAssetsPlugin({}),
    ],
  },
}
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
  optimization: {
    minimizer: [
      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true }),
      new OptimizeCssAssetsPlugin({}),
    ],
  },
}

用了 CSS 优化,必须同时优化 JS,否则 JS 不会压缩成一行。

SplitChunks(抽离公共模块) ​

当多个入口文件引入了相同模块时,抽离出来只打包一份:

js
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          chunks: 'initial',
          minSize: 0,
          minChunks: 2, // 引用两次即抽离
        },
        vendor: {
          priority: 1,
          test: /node_modules/,
          chunks: 'initial',
          minSize: 0,
          minChunks: 2,
        },
      },
    },
  },
}
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          chunks: 'initial',
          minSize: 0,
          minChunks: 2, // 引用两次即抽离
        },
        vendor: {
          priority: 1,
          test: /node_modules/,
          chunks: 'initial',
          minSize: 0,
          minChunks: 2,
        },
      },
    },
  },
}

懒加载 ​

使用 webpack 内置的 import() 语法,返回 Promise:

js
const btn = document.createElement('button')
btn.addEventListener('click', function () {
  import('./a.js').then((data) => {
    console.log(data.default)
  })
})
const btn = document.createElement('button')
btn.addEventListener('click', function () {
  import('./a.js').then((data) => {
    console.log(data.default)
  })
})

如需兼容:

bash
yarn add @babel/plugin-syntax-dynamic-import -D
yarn add @babel/plugin-syntax-dynamic-import -D
js
options: { plugins: ['@babel/plugin-syntax-dynamic-import'] }
options: { plugins: ['@babel/plugin-syntax-dynamic-import'] }