浅谈webpack优化

1,083 阅读2分钟

为什么要优化?

伴随着项目越来越大,相应的webpack的构建时间越来越久,使得我们不得不考虑性能优化的问题;

如何进行分析?

这里列出笔者常用的工具,仅作参考

  • 时间分析:speed-measure-webpack-plugin
  • 体积分析:webpack-bundle-analyzer

src=http___img2018.cnblogs.com_blog_1282901_201904_1282901-20190429141741264-980049255.png&refer=http___img2018.cnblogs.jpeg

优化策略

打包时间:

  1. 搜索方面
  • 合理使用resolve
module.exports = {
  // ...
  resolve: {
    extensions: ['.js', '.jsx'], // 自动带上后缀查找文件
    mainFiles: ['index', 'list'],
    alias: {  // 别名,路径的映射,减少查找的范围
      alias: path.resolve(__dirname, '../src/common'),
    },
    modules: [
      path.resolve(__dirname, 'node_modules'), // 指定当前目录下的 node_modules 优先查找
      'node_modules', // 将默认写法放在后面
    ]
  },
}
  • include/exclude
const path = require('path');
module.exports = {
    //...
    module: {
        rules: [
            {
                test: /\.js[x]?$/,
                use: ['babel-loader'],
                include: [path.resolve(__dirname, 'src')],// 指定要包含的文件
                exclude: /node_modules/, // 不需要包含node_modulues 模块
                
            }
        ]
    },
}

  1. 使用缓存提升二次打包速度(将结果缓存到磁盘中,再次构建时,会进行对比,如果文件较之前的没有发送改变,则直接读取缓存)
  • 在性能消耗较大的loader之前添加==cache-loader==,将结果缓存到磁盘中;
module.exports = {
    //...
    module: {
        //项目中,babel-loader耗时比较长,所以给它配置了`cache-loader`
        rules: [
            {
                test: /\.jsx?$/,
                use: ['cache-loader','babel-loader']
            }
        ]
    }
}

  • loader开启cacheDirectory=true 缓存
module.exports = {
    //...
    new HappyPack({
      loaders: ['babel-loader?cacheDirectory=true'],
    })

}
  • 静态资源开启缓存 ==(hash, chunkhash, contenthash)==
    hash:所有的文件哈希值都相同;
    chunkhash:根据不同的入口文件(Entry)进行依赖文件解析、构建对应的 chunk,生成对应的哈希值;
    contenthash: 计算与文件内容本身相关,主要用在css抽取css文件时;
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 
module.exports = {
  mode: "production",
  entry: {
    index: "./src/index.js",
    chunk1: "./src/chunk1.js"
  },
  output: {
    filename: "[name].[chunkhash].js" // 不同的入口文件,对应不同的chunk
  },
  module: { 
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader, 
          "css-loader"
        ]
      }
    ]
  },
  plugins: [ 
    // 提取css插件
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash].css"  // 与文件内容本身相关联
    })
  ]
};
  1. 开启多进程
HappyPack
thread-loader(webpack4及之后推荐使用)
module.exports = {
 // ...
 module: { 
    rules: [
      {
        test: /.js$/, //对所有js后缀的文件进行编译
        use: [
          // 'babel-loader'
          'happypack/loader',
        ],
      }
    ]
  },
  plugins: [ 
    new HappyPack({
      loaders: ['babel-loader'],
    }),
  ]
}
module.exports = {
 // ...
 module: { 
    rules: [
      {
        test: /.js$/, //对所有js后缀的文件进行编译
        include: path.resolve('src'), //表示在src目录下的.js文件都要进行一下使用的loader
        use: [
          'babel-loader',
          {
            loader: 'thread-loader',
            options: {
              workers: 3,
            },
          }
        ],
    }
  ]
}

  1. 使用(动态链接库) DLLPlugin 和 DLLReferencePlugin 单独打包不经常更新的第三方库
DLL缓存前端缓存
把公共代码打包为 DLL 文件存到硬盘里把常用文件存到硬盘/内存里
第二次打包时动态链接 DLL 文件,不重新打包第二次加载时直接读取缓存,不重新请求
打包时间缩短打包时间缩短

打包体积:

  1. JS,CSS代码压缩

    webpack-parallel-uglify-plugin:开始多进程压缩JS
    mini-css-extract-plugin:抽离CSS,生成单独的文件
    optimize-css-assets-webpack-plugin:压缩CSS文件
  2. 开启 tree-shaking(webpack4 默认开启)

  3. 静态资源压缩

    对于文件使用file-loader
    对于图片使用url-loader,limit属性可设置大小
module.exports = {
    // ...
    rules: [
        // ...
        {
        test: /\.(png|svg|jpg|gif)$/,
        use: [
            // ...
            {
                loader: 'url-loader', //是指定使用的loader和loader的配置参数
                options: {
                    limit:500,  //是把小于500B的文件打成Base64的格式,写入JS
                    name: 'images/[name]_[hash:7].[ext]',
                }
            }
        ]
        }
    ]
}

  1. splitCHunksPlugin 代码分割,抽离公共业务代码
optimization: {
  splitChunks: {
     chunks: "async", // 必须三选一: "initial" | "all"(推荐) | "async" (默认就是async)
     minSize: 30000, // 最小尺寸,30000
     minChunks: 1, // 最小 chunk ,默认1
     maxAsyncRequests: 5, // 最大异步请求数, 默认5
     maxInitialRequests : 3, // 最大初始化请求书,默认3
     automaticNameDelimiter: '~',// 打包分隔符
     name: function(){}, // 打包后的名称,此选项可接收 function
     cacheGroups:{ // 这里开始设置缓存的 chunks
         priority: 0, // 缓存组优先级
         vendor: { // key 为entry中定义的 入口名称
             chunks: "initial", // 必须三选一: "initial" | "all" | "async"(默认就是async) 
             test: /react|lodash/, // 正则规则验证,如果符合就提取 chunk
             name: "vendor", // 要缓存的 分隔出来的 chunk 名称 
             minSize: 30000,
             minChunks: 1,
             enforce: true,
             maxAsyncRequests: 5, // 最大异步请求数, 默认1
             maxInitialRequests : 3, // 最大初始化请求书,默认1
             reuseExistingChunk: true // 可设置是否重用该chunk
         }
     }
  }
 },

网络请求方面

  1. 配置externals,静态文件使用CDN加速静态资源的加载
// 在 index.html 中手动引入 cdn 链接。

// webpack.js
module.exports = {
  externals: {
    'vue': 'Vue',
    'element-ui': 'ELEMENT',
    'at-ui': 'at'
  }
}