Webpack 优化与前端优化方案记录

性能优化

工程化相关(webpack):

  • 提取公共代码(Webpack4 webpack.optimize.splitChunks,mode:production已默认配置)
    拆分出入口文件main.js中import导入的依赖,不包括异步加载(() => import())的依赖。
  • 组件懒加载 import()(关于为什么使用import却没有懒加载,请移步至:import()分片问题
    vue  "component: () => import()"
  • 按需引入echarts,elementUI,antdesign,lodash等包。
    echarts4/5按需引入官网例子不相同。 echarts5官网按需引入例子
    lodash: babel-plugin-lodash + lodash-webpack-plugin。 lodash按需引入
  • 代码压缩,
    1. 压缩JS:UglifyJS/UglifyES 。TerserWebpackPlugin(v5自带)(webpack production模式会自动压缩)
    2. 提取CSS:MiniCssExtractPlugin(v4,v5)
    2. 压缩CSS:cssnano ,CssMinimizerWebpackPlugin(v5)
    3. 开启gzip,brotli,sdchdeflate,... CompressionWebpackPlugin(v5)。具体支持什么压缩方式,需要服务支持。
  • Webpack 配置externals,并在index.html用<script>引入依赖的min.js文件,vue,echarts等。不打包,也提升了构建速度。
  • 使用CDN加载静态资源(同上配置externals)
    • CDN资源通过<script>标签引入。如果首屏没有使用到,则可以defer。
  • prerender-spa-plugin 设定预加载页面 (前端SEO优化)

prerender-spa-plugin 利用了 Puppeteer 的爬取页面的功能。原理是在 Webpack 构建阶段的最后,在本地启动一个 Puppeteer 的服务,访问配置了预渲染的路由,然后将 Puppeteer 中渲染的页面输出到 HTML 文件中,并建立路由对应的目录。引用自[参考资料2]

  • web-webpack-plugin为每个js生成一个入口文件 AutoWebPlugin插件的使用(每页都是首屏,相当于不要SPA特性)(很久没更新了,低版本webpack可能有用
  • 使用 Magic comment(Webpack4.6.0+)[参考资料1]
    /* webpackPreload: true */预加载首屏需要的资源 (基于<link> preload)
    /* webpackPrefetch: true */预下载即将可能用到的资源(基于<link> prefetch)
  • 雪碧图 svg-sprite-loader (减少图片请求数)
  • 使用url-loader(file-loader)将小图片通过base64的方式嵌入html(减少图片请求数)
  • Webpack 5不需要使用url-loader,使用自带资源模块替代url-loader的能提升构建速度。
  • 开启Tree Shaking去除无用代码,优化文件大小,配置optimization.usedExports = true  仅支持es module

使用es module语法import {a, b} from './util.js' 引入。假设a,b是function。
若代码中使用了a() ,则打包时会抽取出util.js的function a(){}的部分代码。util.js中的其他未使用的function代码不会打包。(可以优化lodash-es等工具类)
若util.js用export default导出。则无法触发TreeShaking。

打包后:

unesed harmony export 未使用的function不打包

  •  babel 配置可能导致打包过大 (babel入门见参考资料8) 
    module.exports = {
      presets: [
        ["@babel/preset-env",
          {
            //打包不同的模块cmd,umd,cjs,esm等,尤其umd代码会比较多|
            modules: false, // false 代表esm
            //按需引入需要polyfill的内容,@babel/polyfill已废弃,详细百度@babel/polyfill与core-js
            useBuiltIns: 'usage', // webpack中使用usage会有点问题
            corjs: 3.x.x, // 注意:不带小版本则视为3.0.0。使用useBuiltIns需要配置这个,默认为corejs2
            targets: {
              // 需要兼容的浏览器版本,众所周知,浏览器版本越低,需要polyfill的代码越多,与babel有关的代码越多。
              chrome: '84',
            }
          }
        ]
      ]
    }
    babel合理配置浏览器版本,一般版本越高,polyfill代码越少,按实际情况配置
    babel 配置plugin-transform-runtime,用于减少重复引入的polyfill
  • 使用DefinePlugin 通过编译的方式,减少代码体积。如__VUE_OPTIONS_API__: 'false'。
  • core-js(swc)
    • 根据需要引入 core-js/stable 可以减少core-js 大小。
    • 不要放在webpack/rspack 配置文件中的entry中。
      如 entry: { app: ['core-js','./src/indexjs'] },这样在swc配置兼容chrome 版本会没用。
      要在 js 的文件头部 import 'core-js/stable' 。 

偏前端:

  • 懒加载:图片懒加载,滚动到底部后加载
  • 虚拟滚动:一般用于表格、列表、树等每个元素等高的场景。不分页展示大量数据方案。
  • 分批加载:可基于requestAnimationFrame 分帧加载。通俗点说就是,长页面,由上至下,一个个加载,后一个(一批)元素等前一个(一批)元素加载完成后再插入dom。
  • preload / prefetch  link标签的属性 。预下载,预拉取资源。
  • 合理使用防抖、节流。作用于请求:有利于缓解服务器压力。作用于onresize,有利于减少浏览器重绘重排。
  • 合理使用事件委托,不需要每个元素上加事件。
  • 合理使用前端持久化localStorage/sessionStorage/indexedDB
  • 合理使用WebSocket(需服务端支持),取代传统轮询。
  • 合理采用loading动画,骨架屏等。
  • 合理使用Web Worker开启多线程,进行复杂CPU密集任务的计算。
  • 数据采样:折线图如果点很多,可以考虑每几个点采样一个(平均/最大/最小/不计算)点,因为总体图的趋势是相同的。
  • 序列化大量json提升性能:使用 fast-json-stringify
  • 利用 script 标签的defer将首屏不用的依赖后置执行,以优化LCP。
  • 利用 requestIdleCallback 运行首屏不需要执行的代码,以优化LCP。
  • vue:
  1. 合理使用functional函数式组件。(vue3不需要)
  2. 合理使用keep-alive 缓存组件。
  3. data中不需要响应式的大对象
    vue2:使用Object.freeze()包装,以减少vue中defineProperty的耗时
    vue3:shallowRef
  4. DefinePlugin 配置
    1. __VUE_OPTIONS_API__: 'false' 表示打包vue不将optionialApi打包进产物。
    2. __VUE_PROD_DEVTOOLS__: 'false' 
    3. __VUE_I18N_LEGACY_API__: 'false' // 用vue-i18n 却不用legacyApi时,可以优化掉legacyApi的代码。(gz减少2.4KB)
    4. __VUE_I18N_FULL_INSTALL__: 'false' // 移除vue-i18n组件指令代码。(gz减少1.2KB)
  • 注意减少重绘重排
    1. dom.style.cssText = 'color:#000;' // 一次设置完css
    2. 合理使用 DocumentFragment 
    3. HTMLElement.offset/client/scroll[Height/Width] 访问这些属性会造成浏览器重绘。因此不要多次重复通过e.target.offsetWidth 这样的方式获取元素的信息。可以先保存到临时变量。
  • css
    1. content-visibility 可提升页面渲染速度  (chrome > 85),对table标签无效。content-visibility:auto,表示,若该元素在用户可见区,则渲染其子元素。否则不渲染。
    2. 尽量使用transform位移元素,而非top/left/right/bottom/margin/padding等  
    3. will-change 优化动画(实验性)
    4. contain 属性
    5. border-collapse: separate 性能更好

高端性能方案

  • WebAssambly(wasm)

  • WebWorker 开启多线程cpu执行密集任务

  • ServiceWorker (PWA) 离线应用解决方案

  • asm.js (旧)

移动端web

偏服务端:

  • 启用HTTP/2 (使用HTTP/2就可以不做,业内的一些在http1.1时做的,关于减少请求个数的优化,需要服务器支持)
  • 开启gzip, brotli 等压缩。请求头Accept-Encoding,响应头Content-Encoding 。这些一般由服务端的框架或插件提供,不用手写。如koa插件koa-compress
  • 合理使用浏览器缓存。强缓存、协商缓存。
  • 服务端在返回html页面时将一些基础数据嵌入html中,以减少ajax请求数量。(前后端不分离)

提升构建速度

  • Webpack 配置 resolve.extensions常用的后缀放最前面。开发时导入其他模块时尽量带上后缀,此次导入会跳过自动加后缀这一步。
  • Webpack loader配置项中配置include,exclude指定文件路径,缩小搜索范围。
  • Webpack  DLLPlugin / DLLReferencePlugin //预打包,预先打包指定的文件(一般是一些不会变动的依赖),之后打包的时候就不会再编译这些文件。
  • 多进程执行loader插件(项目小就不要用,反而可能由于多进程通信原因增加打包时间)
    1.happypack //(很久没更新了,低版本webpack可能有用) 
    2.thread-loader、cache-loader // webpack4官网文档 请仅在耗时的 loader 上使用
    3.parallel-webpack
  • fast-sass-loader // 多进程sass-loader(低版本webpack可能有用
  • 多进程多实例并行压缩 [参考资料5]

  1. ParallelUglifyPlugin // 多进程UglifyJS压缩(低版本webpack可能有用) 
  2. uglifyjs-webpack-plugin 开启 parallel 参数
  3. terser-webpack-plugin 开启 parallel 参数 (推荐使用这个,支持 ES6 语法压缩)
  • hard-source-webpack-plugin // 缓存第一次打包的结果(/node_modules/.cache下),之后再打包,速度起飞。webpack5用不了,已经集成了打包缓存如下
  • webpack5 配置cache.type='filesystem' // 开发模式默认为'memory'
    配成filesystem效果比默认明显,会有node-sass报错,换成dart-sass解决
    配成filesystem可能导致node_modules改变会不生效。此时应该清除node_modules/.cache目录的缓存,重新启动。
  • babel-loader开启缓存 // cacheDirectory: true
  • sass 编译实在很慢的话,考虑使用node-sass替换sass(dart-sass),但是node-sass在安装时容易出问题。(据说node-sass(libsass C/C++) 比dart-sass(dart-VM) 快,未验证。)
  • typescript
    • ts-loader 配置 transpileOnly: true,跳过语法检查。
    • babel @babel/preset-typescript 替换ts-loader 编译ts。与ts-loader会有一点点区别。
  • 使用esbuild编译js/ts,esbuild-loader
    如果项目用了babel 转换js代码,并使用了babel一些插件如(babel-plugin-import),则esbuild可能没有等价替换的插件。因此,esbuild目前并不能完全取代babel
    使用esbuild-loader转换js/ts前后速度对比:
  • 使用swc 替换babel转换代码。使用esbuild压缩代码。swc-loader / esbuild-loader。
    我尝试了一下这种方案:Webpack + swc + esbuild 优化构建速度尝试 效果还可以。
  • vue-cli
    • lintOnSave: false // 关闭eslint校验
    • productionSourceMap: false // 关闭正式包source-map
  • webpack.config.js module.noParse 指定不解析的库

npm 依赖优化

对于发布正式时,在服务器中执行 npm run build 构建的项目:

  • 将webpack这类打包工具安装到 dependencies。
  • 将eslint 这类依赖工具安装到 devDependencies。
  • 线上构建脚本npm i 替换为 npm i --production,仅安装dependencies 中指定的模块。

使用轻量依赖替换

大 -> 小

  • lodash -> lodash-es
  • decimal.js -> decimal.js-light
  • moment -> dayjs
    moment可通过webpack.ContextReplacementPlugin()移除非中文国际化

Ant-design-vue

  • 用antd-dayjs-webpack-plugin插件使moment替换为day.js 。Ant-design-vue 替换moment为dayjs方式记录_JA+的博客-CSDN博客
  • 使用webpack.ContextReplacePlugin,优化moment语言包(若使用dayjs替换可无优化)
  • 通过配置webpack alias,使antdv图标按需引入。
    // yourIcon.js
    export { default as CaretDownOutline } from '@ant-design/icons/lib/outline/CaretDownOutline'
    resolve.alias:
    '@ant-design/icons/lib/dist$': path.resolve(__dirname, 'yourIcon.js')

工具

  • webpack-bundle-analyzer //热门打包分析工具,可以看到打包后的文件大小。
  • speed-measure-webpack-plugin  //打印各loader、plugin的耗时

配置dev-tool: 'source-map' 打出来的包就不会使用eval函数封装代码

其他

参考资料

  1. webpack预加载
  2. 前端prerender-spa-plugin预渲染
  3. AutoWebPlugin插件的使用
  4. 深入浅出Webpack 第4章 优化
  5. webpack 打包优化的四种方法(多进程打包,多进程压缩,资源 CDN,动态 polyfill)
  6. webpack打包极限优化
  7. 揭秘 Vue.js 九个性能优化技巧
  8. Babel 配置用法解析
  9. 别用babel-polyfill了,教你用core-js@3兼容IE浏览器 - 全情海洋 - 博客园 (cnblogs.com)
  10. css3动画性能优化(转载) - 知乎
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值