四时宝库

程序员的知识宝库

Webpack 性能优化全攻略

Webpack 作为现代前端开发里不能少的工具,在项目构建和资源管理这块起着关键作用。在前端开发越来越复杂的现在,做性能优化特别重要。

先从开发效率这块来说,一个没优化过的 Webpack 配置可能会让构建的时间变得很长。比如说,在大项目里,要是每次改代码都得花好多时间等着构建完,那会严重影响开发的进度。根据一些实际项目的数据统计,没优化的 Webpack 构建可能得要几分钟甚至更长时间,但是通过合理的性能优化办法,能把构建时间缩短到几十秒甚至更短。这样能让开发人员更快看到改了代码后的效果,把开发效率提上去。

其次,性能优化对用户体验也特别关键。优化后的 Webpack 能把打包文件变小,这样就能让用户加载的时间变短。有研究说,每少一秒的加载时间,都可能让用户留存率明显提高。比如说,当页面加载时间从 5 秒降到 3 秒,用户的满意度和转化率可能会提高 20%还多。通过像代码压缩、懒加载、资源优化这些办法,Webpack 能有效地把打包文件的大小变小,把页面的加载速度提上去,给用户更顺溜的体验。

另外,性能优化对提高项目的可维护性也有帮助。一个优化得好的 Webpack 配置能让项目的结构更清楚,代码更好理解也好改。比如说,通过合理地安排模块、用别名还有优化加载器的配置,能把代码的复杂程度降低,把维护的成本也降下来。同时,优化后的构建过程更稳当,出错的可能性也变小了。

总的来说,Webpack 性能优化对提高开发效率、让用户体验变好还有提高项目的可维护性都很重要。在前端开发里,咱们得重视 Webpack 的性能优化,用有效的办法把项目的整体质量提上去。

二、提高打包速度的方法

(一)优化 loader 的文件搜索范围

通过 include 和 exclude 参数来限定 babel-loader 的作用范围,少做点没必要的转译操作。 咱们能在 Webpack 配置文件里,针对 babel-loader,用 include 参数指明要编译的文件夹,比如说 include: path.resolve(__dirname, '../src'),这样能保证只给项目的源代码做转译。同时,用 exclude 参数把不需要处理的文件夹排除掉,像 exclude: /node_modules/,省得对特别大的第三方库做没必要的转译。这么弄能明显减少 babel-loader 的处理范围,把构建速度提上去。有统计说,在一个中等规模的项目里,合理配置 include 和 exclude 参数以后,babel-loader 的处理时间能减少 30%左右。

合理配置 resolve 参数,能少做点路径查询的工作。

resolve 参数能把模块的引入方式变简单,把路径查找的效率提高。比如说,配置 extensions: ['.js', '.jsx'],在代码里引入模块的时候,Webpack 会先试着找.js 文件,找不到再找.jsx 文件,能少点没必要的报错和查找时间。同时,配置 mainFiles: ['index', 'child'],Webpack 会先试着找 index 文件,没有的话再找 child 文件,把默认文件的查找顺序优化了。另外,还能用 alias 配置给模块起个别名,像 alias: { child: path.resolve(__dirname, '../src/a/b/c/child')},这样能把模块的引入路径变简单,把开发效率提高。不过得注意,extensions 和 mainFiles 不建议配置太多,要不就会有额外查询花时间的问题。

(二)缓存加载器执行结果

用 cache-loader 把耗时长的 loader 的执行结果缓存起来,让后面的构建速度变快。

cache-loader 是个特别实用的工具,每次加载源文件之前它都会查查有没有缓存的结果,要是有,就直接用,没有的话才做实际的加载操作,然后把结果存到缓存里。对一些耗时很长的 loader,像 babel-loader,用 cache-loader 能让二次构建的速度快好多。在实际的项目里,用了 cache-loader 以后,二次构建的时间能缩短 50%甚至更多。配置的办法也简单,把 cache-loader 放在比较费时间的 loader 前面,比如说:{ test: /\\.js$/, use: [ 'cache-loader', 'thread-loader', 'babel-loader' ] }。

(三)多进程打包

用 thread-loader 开多线程来处理文件。

thread-loader 是 Webpack 4 官方推荐的多进程打包工具。把 thread-loader 放在别的 loader 前面,放在它后面的 loader 就会在一个单独的 worker 池里运行,一个 worker 就是一个 Node.js 进程。每个单独进程处理的时间上限是 600ms,各个进程的数据交换也会限制在这个时间里。比如说:{ test: /\\.js$/, exclude: /node_modules/, use: [ 'thread-loader', 'babel-loader' ] }。在大项目里,用 thread-loader 能明显把打包速度提上去,根据实际测试,构建时间能缩短 30%以上。

用 HappyPack 插件来实现多线程解析和构建文件。

HappyPack 也是能实现多线程打包的一种办法。它能把任务分给好多子进程,把构建速度提上去。用 HappyPack 的时候,得先装插件,然后在 Webpack 配置文件里做相应的配置。虽说现在 thread-loader 更推荐用,不过在一些特定的情况里,HappyPack 还是能起作用的。

(四)IgnorePlugin 和 noParse 的使用

对比一下 IgnorePlugin 和 noParse 的作用,合理用它们来忽略没用的模块引入,把构建速度提上去。

IgnorePlugin 能在打包的时候忽略特定的模块或者文件。比如说,要是项目里有一些只在特定环境里用的模块,能用 IgnorePlugin 在不需要的环境里忽略它们的引入,这样能少点没必要的打包体积和构建时间。noParse 是用来指定一些不用被解析的模块。比如说,对一些很大的第三方库,要是确定它们不会被改,也不用解析它们的依赖关系,能用 noParse 配置跳过对它们的解析,把构建速度提上去。在实际的项目里,根据具体情况合理用这两个工具,能有效把构建速度提上去。

(五)使用 DllPlugin 提高打包速度

把大型的第三方库打包成单独的文件,把构建效率提上去。

新弄一个 webpack.dll.config.js 配置文件,对第三方模块做有针对性的打包。比如说:module.exports = { mode: 'production', entry: { vendors: ['react', 'react-dom', 'lodash'] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, '../dll') } }。然后通过全局变量把打包后的库露出来,用 add-asset-html-webpack-plugin 插件在 HTML 文件上加上静态资源的引用。同时,用 webpack.DllPlugin 生成映射文件,结合 webpack.DllReferencePlugin,在打包的时候要是发现第三方模块在映射文件里,就不会再去 node_modules 里面打包了。通过这种办法,能大大把构建速度提上去,特别是在大项目里,效果特别明显。

(六)合理使用 sourceMap

按照开发和生产环境的不同需求,平衡好性能和准确性。

source-map 的作用是在报错的时候方便定位到错误代码的位置。不过它的体积可不小,所以在不同的环境设置不同的类型很有必要。在开发环境里,咱们得能准确定位错误代码的位置,配置成 devtool: 'eval-cheap-module-source-map'。在生产环境里,咱们想把 source-map 打开,但是又不想体积太大,能配置成 devtool: 'nosources-source-map'。通过合理设置 source-map 的类型,能在保证开发效率和错误定位准确的同时,尽量把打包体积变小,把性能提高。

(七)结合分析工具分析打包结果

用 stats、speed-measure-webpack-plugin 和 webpack-bundle-analyzer 这些工具来分析打包的过程,找出性能的瓶颈。

speed-measure-webpack-plugin 能测 Webpack 构建的时候各个阶段花的时间,帮咱们找出构建过程里的瓶颈。装好了以后,通过 const smp = new SpeedMeasurePlugin(); module.exports = smp.wrap(prodWebpackConfig)把整个配置对象包起来,就能分析各个插件和 loader 花的时间。webpack-bundle-analyzer 能检查打包以后的体积分布,然后做相应的体积优化。只在打包的时候看体积,所以只要在 webpack.prod.js 里配置就行。通过这些工具的分析,能有针对性地做性能优化,把打包速度提上去。

(八)开发环境内存编译

用 webpack-dev-server 做开发,把产物存在内存里,把开发阶段的页面响应速度提上去。

webpack-dev-server 在开发的时候能把构建的产物存在内存里,省得老是读写文件,把页面的响应速度大大提高了。同时,结合热更新的功能,能做到只更新改了的模块,别的模块不变,把开发效率进一步提高。在配置 webpack-dev-server 的时候,能设置 devServer: { hot: true }打开热更新功能,还有设置 devServer: { contentBase: path.join(__dirname, 'dist') }指定静态资源的目录。这样,在开发的时候,页面的加载速度会明显变快,开发的体验也会更顺。

三、优化打包后的文件

(一)小图片 base64 编码

通过设置 url-loader 的 limit 参数,能把小图片变成 base64 编码,这样就能减少 HTTP 请求的次数。参考文档上说,只有比较小的图片资源适合弄成 base64 编码,因为弄成 base64 编码的图片资源一般是放在 css 里,太大的图片资源转了以后会让 css 文件变大,然后就会影响页面加载的效率。比如说,可以在 webpack.config 文件里像下面这样配置:

module.exports = {  
  entry: "./js/main.js",

  output: {

    path: path.resolve(__dirname, "build"),

    publicPath: "/build/",

    filename: "[name].js",

  },

  module: {

    rules: [

      {

        test: /\.(gif|png|jpg|woff|svg|ttf|eot)$/,

        use: [

          {

            loader: "url-loader",

            options: {

              limit: 500,

              outputPath: "images/",

              name: "[name].[ext]",

            },

          },

        ],

      },

    ],

  },

  devtool: "inline-source-map",

  mode: "development",

  plugins: [],

};


这样每次构建的时候,webpack 都会自己给符合规则的图片资源做 base64 编码的转换。

(二)bundle 加 hash

给打包文件加上哈希值能方便处理缓存的问题。比如说,在 webpack 的配置文件里,可以像这样设置输出的文件名:

output: {  
  path: path.resolve(__dirname, 'dist'),

    filename: 'bundle.[hash].js',

}


这么改完以后,每次改了源码编译都会弄出新的带着哈希值的文件。为了不让 dist 目录里有好多过时的文件垃圾,可以装 clean-webpack-plugin 来自动把旧文件清理掉。

(三)提取公共代码和压缩 CSS

  1. 用 SplitChunksPlugin 能把公共的 js 代码提取出来。在有多入口文件的项目里,难免在不同的入口有一样的部分,把多个 css chunk 合成一个 css 文件能提高效率。比如说,可以在 optimization 配置里这么设置:
optimization: {  
  splitChunks: {

    cacheGroups: {

      commons: {

        name: 'commons',

          chunks: 'initial',

            minChunks: 2,

              minSize: 0,

      },

    },

  },

},


  1. 用 mini-css-extract-plugin 能够抽取和压缩 CSS 代码。先把这个插件装上,然后在配置文件里引进去再配置:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// css,scss,sass,less

{

  test: /\.(sa|sc|c)ss$/,

    use: [

      process.env.NODE_ENV === 'development'

      ? 'style-loader'

      : MiniCssExtractPlugin.loader,

      'css-loader',

      'sass-loader',

    ],

}

//最后对应环境下的 plugins 中

new MiniCssExtractPlugin({

  filename: "[name].css",

});


(四)tree shaking

在生产模式下一定得把 tree-shaking 启用了,就编译实际用到的代码,把打包体积变小。webpack 2 正式版本身就支持 ES2015 模块和对没用到的模块的检测能力。webpack 4 正式版把这个检测能力扩展了,通过 package.json 的 "sideEffects" 属性当标记,给编译器提示,告诉它项目里哪些文件是纯的 ES2015 模块,这样就能把文件里没用的部分安全地删掉。比如说,把 mode 配置设成 development,好确定 bundle 不会被压缩,然后设置 optimization:

module.exports = {   
  entry: './src/index.js',

  output: {

    filename: 'bundle.js',

    path: path.resolve(__dirname, 'dist'),

  },

  mode: 'development',

  optimization: {

    usedExports: true,

  },

};


(五)ignorePlugin 忽略无用模块引入

IgnorePlugin 和 noParse 都能用来忽略没用的模块引入,让打包体积变小。IgnorePlugin 能在打包的时候忽略特定的模块或者文件。比如说,要是项目里有一些只在特定环境里用的模块,能用 IgnorePlugin 在不需要的环境里忽略它们的引入。noParse 是用来指定一些不用被解析的模块。比如说,对一些很大的第三方库,要是确定它们不会被改,也不用解析它们的依赖关系,能用 noParse 配置跳过对它们的解析。

(六)Scope Hosting 的特点及使用

Scope Hosting(作用域提升)是 webpack 的一种优化技术,能把好几个模块合到一个函数的作用域里,把函数调用和变量查找的开销减少。它的特点有:让代码体积变小、把运行时的性能提高啥的。使用的办法是在 webpack 配置里启用相关的插件或者选项,具体咋配置得看 webpack 的版本和项目的需求。比如说,可以在高级配置里找找关于 Scope Hosting 的选项去设置。通过 Scope Hosting 能进一步把打包的结果优化好,把项目的性能和加载速度提高。

四、webpack总体概括

Webpack 性能优化这事儿啊,又复杂又特别重要。通过把打包速度提上去,把打包后的文件弄好,咱们能明显把开发效率提高,让用户体验变好,项目也更好维护。

在实际的项目里,开发的人得按照项目具体的需求和特点来挑合适的优化办法。比如说,小项目可能用不着多进程打包的工具,可大项目呢,合理用好多进程打包和缓存的机制能大大把构建时间缩短。要是项目特别看重用户体验,像小图片弄成 base64 编码、bundle 加上 hash 还有模块懒加载这些技术能有效把页面加载速度提上去。

同时呢,Webpack 的性能优化是一直得做的事儿。随着前端技术不停地发展,项目也不停地变,新的优化办法会不断出来。开发的人得一直留意 Webpack 的更新和变化,积极去找新的优化办法,好能适应一直变的开发需求。

比如说,Webpack 5 出来了,带来了更多性能优化的特点,像更好的缓存机制、内置的模块联邦啥的。开发的人可以好好研究研究这些新特点,用到实际项目里,进一步把项目的性能提上去。 反正,Webpack 性能优化是前端开发里少不了的一部分。开发的人得充分明白它的重要性,按照实际项目的需求选合适的优化办法,还得一直找新的优化策略,来做出高效、质量好的前端项目。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接