调优成绩

优化前优化后优化比例
打包体积32.01 MB16.56 MB48.27%
Gzip 体积3.37 MB1.82 MB45.99%
文件数量821976.83%
资源压缩服务器 br 压缩客户端 br 压缩1
压缩级别611(最高)1
构建速度60s +41.1s +31.67%
播放页 FP 耗时显著感知无感知1

(以上数据起源为本地测试,仅供参考)

优化指标

  • 升高播放页 FP 耗时
  • 缩小动态资源申请数量
  • 晋升开发体验
  • 晋升构建产物品质
    (文章基于 webpack@4.46.0)

    问题剖析

    先看我的项目以后的打包剖析后果

以上是用 vue-cli 生成的构建统计报告,它会帮忙剖析包中蕴含的模块们的大小,简略列一下报告信息如下:

  1. 整体打包体积 32.01 MB
  2. ts.worker.js 是 monaco 编辑器的语言反对文件,次要提供 typescript 语法反对,体积 10.92 MB
  3. my-details.js 是播放页文件,网站外围资源文件,体积 6.81 MB
  4. chunk-vendors.js 第三方模块捆绑包,体积 4.14 MB
  5. html.workar.js 是 monaco 编辑器的 html 语法反对文件
  6. css.workar.js 是 monaco 编辑器的 css 语法反对文件
  7. json.worker.js 是 monaco 编辑器的 json 语法反对文件
  8. app.js 我的项目入口文件,体积 1.02 MB
  9. 小文件文件数量太多,加起来靠近百个,导致 http 申请过多

通过剖析总结,定位问题如下:

  1. monaco-editor 是最大的问题,体积占据半壁江山,重大影响加载速度,须要优化
  2. chunk-vendors.js 作为公共模块,形成我的项目必不可少的一些根底类库,降级频率都不高,但每个页面都须要它们,当初它体积过大,应该在正当范畴内拆分成更小一些的 js,以利用浏览器的并发申请,优化首页加载体验。其中蕴含了三个大家伙:elementUI、moment 和 lodash,更是须要独自做优化
  3. my-details.js 和 app.js 作为我的项目次要的资源文件,体积过大,须要优化
  4. 小文件数量太多,须要合并

解决思路

查看下有没有哪些产出是不必要的,在无限的工夫空间和算力下,去除低效的反复(提出公共大模块),进行正当的冗余(小文件容许反复),达到工夫和空间综合考量上的最优。
上面分步骤实现每一个优化项。

monaco-editor 优化

放大体积

咱们业务层面,只须要用到展现性能,其语言编辑性能齐全用不到,因而能够把语言包全副过滤掉,这里须要用到 monaco-editor-webpack-plugin 插件,配置增加插件选项 languages 为反对的语言数组(具体语言查看官网)默认是反对所有语言的,配置此项应该只是去除一些语言的高级个性反对。

new MonacoWebpackPlugin({    languages: [],}),

再次打包后,体积放大至 9.26 MB。

独自打包

monaco-editor 作为一个重量级组件,会扩散很多小文件到各个中央,从而减少文件数量和体积,进而造成流量损失,通过 webpack 的 splitChunks 性能拆分 monaco-editor 为独自独立文件,充分利用浏览器缓存,缩小屡次援用时的耗费。

splitChunks: {  chunks: 'all',  minSize: 20000,  maxAsyncRequests: 30,  maxInitialRequests: 30,  enforceSizeThreshold: 50000,  maxSize: 0,  cacheGroups: {    monacoEditor: {      chunks: 'async',      name: 'chunk-monaco-editor',      priority: 22,      test: /[\/]node_modules[\/]monaco-editor[\/]/,      enforce: true,      reuseExistingChunk: true,    },}

按需加载

通过按需加载,缩小核心内容渲染前的阻塞,转到须要的时候加载。

const monaco = await import('monaco-editor');

开启 prefetch

按需加载后,因为 monaco-editor 自身体量很大,因而在加载编辑器时会呈现长时间无响应景象,采纳 prefetch 预加载计划,在浏览器闲暇时事后下载资源,到用的时候间接取,无效防止无响应状况。

const monaco = await import( /* webpackPrefetch: true;" */ 'monaco-editor');

moment.js 优化

尝试删除 moment.js 语言包后体积仍然很大,最初采纳和 moment.js api 齐全兼容的 dayjs 替换,gzip压缩后仅仅 2kb

(因替换工作为全局,可能会呈现和 moment.js 相干性能的 bug )

element-ui 优化

独自打包

实践上 UI 组件库也能够放入 chunk-vendors.js 中,但它切实是过大,可能比 libs 里所有的包加起来还要大不少,而且 UI 组件库的更新频率也绝对比 chunk-vendors.js 要更高一点。Element-UI 组件库作为 UI 组件,应从 chunk-vendors.js 中分离出来,独自打包为 chunk-elementUI.js,如图所示打包后体积为 1.67 MB

按需引入

依照官网按需引入形式,只引入应用的组件,缩小体积。

lodash.js 优化

按需引入

应用 webpack 插件 lodash-webpack-plugin 和 babel 插件实现按需打包 lodash

const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');config.plugin('loadshReplace').use(new LodashModuleReplacementPlugin());
module.exports = {  presets: ['@vue/cli-plugin-babel/preset'],  plugins: [    'lodash',  ],};

优化后,lodash 基本上做到无感知存在

svgIcon优化

独自打包

svgIcon 组件库作为高频更新且援用超多的库,应独自分出为 chunk-svgIcon.js,如图所示打包后体积为 561 kb

删除 use-zh.svg

通过代码审查发现体积最大的 use-zh.svg 在我的项目中当初并未应用,所以删除

雪碧图

将所有 svg 合成雪碧图,缩小申请次数,应用 svg-sprite-loader 实现

const svgRule = config.module.rule('svg'); // 革除已有的所有 loader,如果你不这样做,接下来的 loader 会附加在该规定现有的 loader 之后。svgRule.uses.clear(); // 增加要替换的 loadersvgRule  .test(/.svg$/)  .include.add(path.resolve(__dirname, 'src/components/svgIcon/svg'))  .end()  .use('svg-sprite-loader')  .loader('svg-sprite-loader')  .options({    symbolId: 'icon-[name]',  })  .end();

压缩 svg (未执行)

因为压缩过后的 svg 图会存在去掉 fill 的状况,因而这一步骤并未执行

代码压缩

采纳 terser 插件(webpack4 官网举荐)进行 js 和 css 代码压缩,同时去掉生产环境的正文和 console 信息

config.optimization.minimizer('terser').tap(options => {    const compress = {      warnings: false,      drop_console: true,      drop_debugger: true,      pure_funcs: ['console.log'],    };    const initCompress = options[0].terserOptions.compress;    options[0].terserOptions.compress = { ...initCompress, ...compress };    return options;});

图片压缩(未执行)

应用 webpack 插件 image-webpack-loader 对所有图片进行压缩,然而该插件依赖于零碎环境,在不同环境下可能呈现装置失败,编译失败等状况,咱们我的项目目前图片资源较少,所以临时不必。

开启 br 压缩

以后 br 压缩是在 nginx 服务器进行且并未缓存资源,因而每次申请都须要对资源进行压缩之后收回,思考服务器性能,压缩级别设置为 6。
当初改为客户端压缩,服务器在接管申请时间接把压缩文件收回,缩小服务器压力。同时客户端压缩能够把压缩级别调整至最高的 11,整体资源大小会再次降落,应用 compression-webpack-plugin 实现。

const CompressionWebpackPlugin = require('compression-webpack-plugin');if (!isDev) {  plugins.push(    new CompressionWebpackPlugin({      filename: '[path].br[query]',      algorithm: 'brotliCompress',      test: /.(js|css|json|txt|html|ico|svg)(?.*)?$/i,      compressionOptions: {        params: {          [zlib.constants.BROTLI_PARAM_QUALITY]: 11,        },      },      threshold: 1024,      minRatio: 0.99,      //删除原始文件只保留压缩后的文件      deleteOriginalAssets: false,    }),  );}

多线程执行 loader

开启 parallel 为 Babel 或 TypeScript 应用 thread-loader

parallel: require('os').cpus().length > 1

打包缓存

采纳 HardSourceWebpackPlugin 插件为模块提供两头缓存,缓存默认的寄存门路是: node_modules/.cache/hard-source。配置 hard-source-webpack-plugin,首次构建工夫没有太大变动,然而第二次开始,构建工夫大概能够节约 80%。
可能带来的问题,修复方法在这 hard-source-webpack-plugin
上面链接用于解决 hash 失落问题
https://github.com/mzgoddard/...

其余批改项

  • 提取环境变量 const` isDev = process.env.NODE_ENV === 'development'; `
  • 减少打包速度检测插件 SpeedMeasurePlugin

最终报告

报告图: