FE Workflow
standard-optimize>webpack

构建性能

热替换

module.exports = {
  devServer: {
    hot: true,
  },
}

减少模块解析

// 针对没有依赖的模块进行不解析配置
module.exports = {
  mode: 'development',
  module: {
    noParse: /jquery/,
  },
}

优化 loader

  • 限制 loader 应用范围
  • 缓存 loader 结果 cache-loader
  • loader 运行开启多线程 thread-loader
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        // lodash 本身使用旧语法,在转化时不需要再进行解析
        // exclude: /lodash/,
        include: /src/,
        use: ['cache-loader', 'thread-loader', 'babel-loader'],
      },
    ],
  },
}

传输性能

externals & CDN

vue.config.js
index.html
let externals = {}
let cdn = {}
if (process.env.NODE_ENV === 'production') {
  externals = {
    lodash: '_',
    echarts: 'echarts',
  }
  cdn = {
    js: [
      'https://cdn.bootcdn.net/ajax/libs/lodash/4.17.21/lodash.min.js',
      'https://cdn.bootcdn.net/ajax/libs/echarts/5.1.2/echarts.min.js',
    ],
  }
}

module.exports = {
  configureWebpack: {
    externals,
  },
  chainWebpack: (config) => {
    config.plugin('html').tap((args) => {
      args[0].cdn = cdn
      return args
    })
  },
}

自动分包

webpack.config.js
module.exports = {
  mode: 'production',

  optimization: {
    splitChunks: {
      // 分包配置

      chunks: 'all',
      // maxSize: 60000,
      // minChunks: 2,
      // minSize: 30000,

      cacheGroups: {
        // 属性名是缓存组名称,会影响到分包的chunk名
        // 属性值时缓存组的配置,缓存组继承所有的全局配置,也有自己特殊的配置
        vendors: {
          test: /[\\/]node_modules[\\/]/, // 匹配到相应模块时,将这些模块进行单独打包
          priority: -10, // 缓存组优先级,优先级越高,该策略越先进行处理,默认值0
        },
        default: {
          minChunks: 2, // 覆盖全局配置,将最小的chunk引用数改为2
          priority: -20, // 优先级
          reuseExistingChunk: true, // 重用已经被分离出去的chunk
        },
        // 举例:样式chunks
        style: {
          minChunks: 2,
          test: /\.css$/,
          priority: 0,
        },
      },
    },
  },
}

手动分包

  • 开启 output.library 暴露公共模块
  • 用 DllPlugin 创建资源清单
  • 用 DllReferencePlugin 使用资源清单
webpack.dll.config.js
webpack.config.js
package.json
index.html
// webpack.dll.config.js
const path = require('node:path')
const webpack = require('webpack')

module.exclude = {
  mode: 'production',
  entry: {
    lodash: ['lodash'],
    jquery: ['jquery'],
  },
  output: {
    filename: 'dll/[name].js',
    library: '[name]',
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(__dirname, 'dll', '[name].manifest.json'),
      name: '[name]',
    }),
  ],
}

单模块体积优化

  • 压缩代码
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
  mode: 'production',
  optimization: {
    // 是否启用压缩,生产环境默认启用
    minimize: true,
    minimizer: [
      // 默认内置 JS 压缩
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
          },
        },
      }),

      // CSS 压缩
      new CssMinimizerPlugin(),
    ],
  },
}
  • Tree Shaking
    • mode: 'production'
    • 使用 export {} 导出,而不是用 export default {} 导出
    • 使用 import {} from '..' 导出,而不是用 import from '..' 导出
    • 标记文件副作用
    {
      "sideEffects": true # 所有模块都有副作用
      "sideEffects": [ !"*.css" ] # 指定模块没有副作用
    }
    

懒加载

util.js
index.js
export { chunk } from 'lodash-es'

GZIP 预压缩

webpack
nginx
// 移除服务器的压缩时间
// npm install --save-dev compression-webpack-plugin
const CompressionPlugin = require('compression-webpack-plugin')

module.exports = {
  plugins: [
    new CompressionPlugin({
      filename: '[path][base].gz', // 输出文件名,可以自定义
      algorithm: 'gzip', // 使用的压缩算法
      test: /\.(js|css|html|svg)$/, // 匹配要压缩的文件类型
      threshold: 10240, // 只处理大于 10KB 的文件
      minRatio: 0.8, // 只有压缩率小于这个比例的文件才会被处理
    }),
  ],
}

其他优化

Bundle Analyzer

base
webpack
package
pnpm add -D webpack-bundle-analyzer
构建性能
热替换
减少模块解析
优化 loader
传输性能
externals & CDN
自动分包
手动分包
单模块体积优化
懒加载
GZIP 预压缩
其他优化
Bundle Analyzer