前端开发体验提升-webpack打包速度优化(30s -> 13s,第2次打包到3s),同时加快热更新

1,224 阅读3分钟

前端开发体验提升-webpack打包速度优化

本篇主题是:webpack打包速度优化

准备分2步:

  1. 如何发现打包速度慢?(精确到哪个plugin慢,哪个loader慢)
  2. 如何解决慢的问题?

进入正题

如何发现打包速度慢?(精确到哪个plugin慢,哪个loader慢)

安装插件:speed-measure-webpack-plugin: www.npmjs.com/package/spe…

  • 注:webpack5以上,可能会有问题

配置speed-measure-webpack-plugin的方法:

// 修改你的 webpack 配置

// 把以下这种对象
const webpackConfig = {
  entry: '',
  output: '',
  plugins: [new MyPlugin(), new MyOtherPlugin()],
};

// 写成以下这种
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();

const webpackConfig = smp.wrap({
  entry: '',
  output: '',
  plugins: [new MyPlugin(), new MyOtherPlugin()],
});

npm run build 后,效果如下

image.png

可以看到对应plugin和loader的耗时

知道了哪些地方慢之后,开始想办法解决慢的地方

如何解决慢的问题?

首先把官网推荐的优化操作,先给弄下来

(有些优化操作 新版本webpack已经内置了):webpack.docschina.org/guides/buil…

整理出几个要操作的小点:

  1. 使用最新的版本(新版会有性能优化)
    • 比如 用最新版的:webpack,node,各种loader和plugin
  2. 通过使用 include 字段,仅将 loader 应用在实际需要将其转换的模块:
    const path = require('path');
    
    module.exports = {
      //...
      module: {
        rules: [
          {
            test: /\.js$/,
            include: path.resolve(__dirname, 'src'), // 避免去扫描 node_modules
            loader: 'babel-loader',
          },
        ],
      },
    };
    
  3. Devtool的sourceMap
    • 用于生产(正式)环境中:(none)(或省略 devtool 选项) - 不生成 source map (减小体积)
    • 用于开发(dev)环境中:推荐 eval-source-map

基本的通用方案弄完后,是否就结束了? - 还没结束,还有优化空间,往下看

分析问题:哪些能优化?哪些动不了?

排除法,先看看哪些动不了?(此分析案例是vue工程,react工程也可以类似这么分析)

  • 一般稳定的第三方loader或webpack官方插件,本身就已经做了优化,想动源码改造的话,成本过高,收益不明显。几乎可以忽略,比如:vue-loader,css-loader,sass-loader,less-loader等

除了“基本的开发标配”后,需开发者自己在去检查,项目内是否用了什么会耗时的插件。

举例:以博主接手的老公司项目来说,用上面讲的speed-measure-webpack-plugin插件,分析出能够操作的耗时项(一般是开发者自己额外添加的loader或plugin)

优化1

检测到 _image-webpack-loader@4.6.0@image-webpack-loader took 12.081 secs

  • image-webpack-loader:耗时12s。(其余的均为基本插件和loader,优化空间很小)
  • 了解到image-webpack-loader的作用是压缩图片,并且每次打包都会重新压缩图片,太费时间。所以可以考虑,所有图片一次性压缩好(可以用tinypng.com/ ),然后在这个loader去掉

打包速度优化效果: 30s -> 20s左右

image.png

优化2

打包的文件肯定越少越好,第三方库就没必要每次都去打包了,一般项目内第三方库还不少,完全可以去配置CDN,然后这部分第三方库就不用打包了

配置CDN前:vender包合集有7.71Mb,非常吓人哈

image.png

配置CDN后:vender包合集下降到1.95Mb,还挺给力

image.png

打包速度优化效果: 20s -> 13s左右

image.png

如何配置CDN(webpack工程),可以参考博主的姊妹篇:webpack工程项目中配置第三方库的CDN,并做CDN容错

优化3

插件 hard-source-webpack-plugin : www.npmjs.com/package/har…

作用:它为模块提供了一个中间缓存步骤。为了看到结果,你需要使用这个插件运行 webpack 两次: 第一次构建将花费正常的时间。第二次建设将是显着的更快。

new HardSourceWebpackPlugin({
  cacheDirectory'node_modules/.cache/hard-source/[confighash]' // 缓存默认的目录。
  // 当更新package.json内的依赖时,如果没生效,可能是这个插件的缓存问题,可以把上面这个路径的缓存包给删除
})

第二次打包时间能优化到了3s左右

优化4

获取打包后,各个资源大小占比情况:www.npmjs.com/package/web…

使用插件:

// npm install -D webpack-bundle-analyzer
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
 plugins: [
   new BundleAnalyzerPlugin()
 ]
}

webpack bundle analyzer zoomable treemap

优化点举例:

  1. 用这个插件可以很轻易的找出一些过大的包,比如loadsh,可以按需引入

     // npm i -D lodash.clonedeep
    
     import clonedeep from 'lodash.clonedeep'
     import('lodash.clonedeep').then(clonedeep => {
    
     })
    
  2. moment语言包也可以按需引入(比如你只需要中文,其他语言可以不要加载)

    const webpack = requrie('webpack')
    
    plugins: [
      // 忽略moment语言包
      new webpack.IgnorePlugin({
        resourceRegExp: /^\.\/locale$/,
        contextRegExp: /moment$/,
      }),
    ]
    

    以上是把所有语言包给屏蔽了,所以需要自己手动引入需要的语言包

    // ./src/index.js
    import 'moment/locale/zh-cn.js' // 最好放上面,先加载语言包
    import moment from 'moment'
    
  3. 有些陌生库你不知道他有多大

  • 比如,博主之前自己写的内部公共组件库内 的一个公共组件加了brace(功能是做一个在线代码编辑器),结果brace包包含了各种语言,打包后还有8M。。。解决方法:只引入业务需要的部分,比如sql,js等

优化5

使用webpack插件DllPlugin

原理:

  • 某些不会改动的代码(如公共库),或体积很大又不常动的代码。把这些代码只需打包一次,下次就不用在打包 直接引用。这样就可以提高打包速度了
    • 比如:把公共库打包成一个单独的库文件,每次只需打包业务代码就行了,这样就减少了公共打包的那部分时间,整体速度得到了提升(此处公共库也可以用CDN替代)
    • 除了公共库,一些体积很大,耗时很长,又不常动的代码,也可以用此插件

如何使用DllPlugin(具体操作):segmentfault.com/a/119000002…

提升热更新方法

  1. 打包速度提升自然可以加快热更新
  2. 考虑代码结构,我们这的项目,每次都显示页面内容之前,都要先去请求get_menu接口,有时候热更新失效,需刷新页面的反应就很慢。
    • 优化手段:可以考虑,开发时,把menu校验给关掉,可以不用等接口返回,直接展示内容。这样每次刷新能快1,2秒

原创整理,有错误可留言,如有用,谢谢点赞~