• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Webpack5优化:提高代码运行性能(Preload、Network Cache、Core-js、PWA)

武飞扬头像
小洋人最happy
帮助3

一、Preload/Prefetch

1.1 为什么

我们前面已经做了代码分割,同时会使用import动态导入语法来进行代码按需加载(我们也叫懒加载,比如路由懒加载就是这样实现的)。

但是加载速度还不够好,比如:是用户点击按钮时才加载这个资源的,如果资源体积很大,那么用户会感觉到明显卡顿效果。

我们想在浏览器空闲时间,加载后续需要使用的资源。我们就需要用上PreloadPrefetch技术。

1.2 是什么

  • Preload:告诉浏览器立即加载资源
  • Prefetch:告诉浏览器在空闲时才开始加载资源

1.2.1 共同点:

  • 都只会加载资源,并不执行
  • 都有缓存

1.2.2 区别:

  • Preload加载优先级高,Prefetch加载优先级低
  • Preload只能加载当前页面需要使用的资源,Prefetch可以加载当前页面资源,也可以加载下一个页面需要使用的资源

1.2.3 问题

兼容性较差

我们可以去 Can I Use 网站查询API的兼容性问题

Preload相对于Prefetch兼容性好一点,如图
学新通
学新通

1.2.4 总结

  • 当前页面优先级高的资源用Preload加载
  • 下一个页面需要使用的资源用Prefetch加载

1.3 怎么样

1.3.1 安装依赖

npm i @vue/preload-webpack-plugin -D

1.3.2 配置

修改config/webpack.prod.js:

const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin')
plugins: [
  // 配置预加载
  new PreloadWebpackPlugin({
    rel: 'preload',// preload兼容性更好
    as: 'script'
  })
],

1.3.3 测试

执行

npm run build

二、Network Cache

2.1 为什么

将来开发时我们对静态资源会使用缓存来优化,这样浏览器第二次请求资源就能读取缓存了,速度很快。

但是这样的话就会有一个问题, 因为前后输出的文件名是一样的,都叫main.js,一旦将来发布新版本,因为文件名没有变化导致浏览器会直接读取缓存,不会加载新资源,项目也就没法更新了。

所以我们从文件名入手,确保更新前后文件名不一样,这样就可以做缓存了。

2.2 是什么

它们都会生成一个唯一的 hash 值。

  • fullhash(webpack4 是 hash):每次修改任何一个文件,所有文件名的hash值都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效
  • chunkhash:根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。我们 jscss是同一个引入,会共享一个hash
  • contenthash:根据文件内容生成hash值,只有文件内容变化了,hash值才会变化。所有文件hash值是独享且不同的

2.3 怎么用

修改config/webpack.prod.jsconfig/webpack.dev.js

output: {
  // 文件输出路径
  // __dirname nodejs变量,代表当前文件的文件夹目录
  path: path.resolve(__dirname, '../dist'),
  // [contenthash:8]使用contenthash,取8位长度
  filename: "static/js/[name].[contenthash:8].js", // 入口文件打包输出资源命名方式
  chunkFilename: "static/js/[name].[contenthash:8].chunk.js", // 动态导入输出资源命名方式
  assetModuleFilename: "static/media/[name].[hash][ext]", // 图片、字体等资源命名方式(注意用hash)
  clean: true, // 自动将上次打包目录资源清空
},
plugins: [
	// 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/[name].[contenthash:8].css",
      chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
    }),
]
学新通

执行

npx webpack

结果如下:
学新通

2.3.1 问题

当我们修改math.js文件再重新打包的时候,因为contenthash原因,math.js文件hash值发生了变化(这是正常的)。

但是main.js文件的hash值也发生了变化,这会导致main.js的缓存失效。

明明我们只修改math.js, 为什么main.js也会变身变化呢?

2.3.2 原因

  • 更新前:math.xxx.jsmain.js引用的math.xxx.js
  • 更新后:math.yyy.jsmain.js引用的math.yyy.js, 文件名发生了变化,间接导致main.js也发生了变化

2.3.3 解决

hash值单独保管在一个runtime文件中。

我们最终输出三个文件:main、math、runtime。当math文件发送变化,变化的是mathruntime文件,main不变。

runtime文件只保存文件的hash值和它们与文件关系,整个文件体积就比较小,所以变化重新请求的代价也小。

修改config/webpack.prod.js文件:

optimization: {
	// 提取runtime文件
    runtimeChunk: {
      name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名规则
    },
}

运行npm run build,查看结果:
学新通
这样就解决了引用文件名变化导致缓存失效的问题了。

三、core-js

3.1 为什么

过去我们使用babeljs代码进行了兼容性处理,其中使用@babel/preset-env智能预设来处理兼容性问题。

它能将ES6的一些语法进行编译转换,比如箭头函数、点点点运算符等。但是如果是async函数、promise对象、数组的一些方法(includes)等,它没办法处理。

所以此时我们 js代码仍然存在兼容性问题,一旦遇到低版本浏览器会直接报错,所以我们想要将 js兼容性问题彻底解决

3.2 是什么

core-js是专门用来做ES6以及以上API polyfill

polyfill翻译过来叫做垫片/补丁,就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该新特性

3.3 怎么用

3.3.1 修改代码

修改main.js,添加promise

// 添加Promise代码
const promise = Promise.resolve()
promise.then(() => {
  console.log('hello promise');
})

3.3.2 配置eslint

安装依赖:

npm i @babel/eslint-parser -D

修改.eslintrc.js

module.exports = {
	...
	parser: "@babel/eslint-parser", // 支持最新的最终 ECMAScript 标准
	...
}

3.3.3 测试

运行

npm run build

此时观察打包输出的js文件,我们发现Promise语法并没有编译转换,所以我们需要使用core-js来进行polyfill

3.3.4 使用core-js

安装依赖:

npm i core-js
3.3.4.1 手动全部引入

修改main.js,引入core-js

import "core-js";

这样引入会将所有兼容性代码全部引入,体积太大了,我们只想引入promisepolyfill

3.3.4.2 手动按需引入

修改main.js,引入core-js

import "core-js/es/promise";

只引入打包promisepolyfill,打包体积更小。但是将来如果还想使用其他语法,还需要手动引入库很麻烦。

3.3.4.3 自动按需引入

修改babel.config.js

presets: [
  [
    "@babel/preset-env",
    // 按需加载core-js的polyfill
    { useBuiltIns: "usage", corejs: { version: "3", proposals: true } },
  ],
],

此时就会自动根据我们代码中使用的语法,来按需加载相应的polyfill了。

四、PWA

4.1 为什么

开发Web App项目,项目一旦处于网络离线情况,就没法访问了。

我们希望给项目提供离线体验。

4.2 是什么

渐进式网络应用程序(progressive web application - PWA):是一种可以提供类似于native app(原生应用程序) 体验的Web App的技术。

其中最重要的是,在离线(offline)时应用程序能够继续运行功能。

内部是通过Service Workers技术实现的。

4.3 怎么样

4.3.1 安装依赖

npm i workbox-webpack-plugin -D

4.3.2 配置文件

修改config/webpack.prod.js

const WorkboxPlugin = require('workbox-webpack-plugin')
plugins: [
	// 配置pwa
    new WorkboxPlugin.GenerateSW({
      // 这些选项帮助快速启用 ServiceWorkers
      // 不允许遗留任何“旧的” ServiceWorkers
      clientsClaim: true,
      skipWaiting: true,
    })
]

4.3.3 修改main.js

// pwa
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker
      .register("/service-worker.js")
      .then((registration) => {
        console.log("SW registered: ", registration);
      })
      .catch((registrationError) => {
        console.log("SW registration failed: ", registrationError);
      });
  });
}

4.3.4 测试

运行指令

npm run build

此时如果直接通过VSCode访问打包后页面,在浏览器控制台会发现SW registration failed

因为我们打开的访问路径是:http://127.0.0.1:5500/dist/index.html,此时页面会去请求service-worker.js文件,请求路径是:http://127.0.0.1:5500/service-worker.js,这样找不到会 404

实际service-worker.js文件路径是:http://127.0.0.1:5500/dist/service-worker.js

4.3.5 解决路径问题

安装依赖

npm i serve -g

serve是用来启动开发服务器来部署代码查看效果的

运行指令

serve dist

此时通过serve启动的服务器我们service-worker就能注册成功了

五、小结

我们从4个角度对webpack和代码进行了优化:

5.1 提升开发体验

  • 使用Source Map让开发或上线时代码报错能有更加准确的错误提示

5.2 提升打包构建速度

  • 使用HotModuleReplacement让开发时只重新编译打包更新变化了的代码,不变的代码使用缓存,从而使更新速度更快
  • 使用OneOf让资源文件一旦被某个loader处理了,就不会继续遍历了,打包速度更快
  • 使用Include/Exclude排除或只检测某些文件,处理的文件更少,速度更快
  • 使用Cacheeslintbabel处理的结果进行缓存,让第二次打包速度更快
  • 使用Thead多进程处理eslintbabel任务,速度更快(需要注意的是,进程启动通信都有开销的,要在比较多代码处理时使用才有效果)

5.3 减少代码体积

  • 使用Tree Shaking剔除了没有使用的多余代码,让代码体积更小
  • 使用@babel/plugin-transform-runtime插件对babel进行处理,让辅助代码从中引入,而不是每个文件都生成辅助代码,从而体积更小
  • 使用Image Minimizer对项目中图片进行压缩,体积更小,请求速度更快(需要注意的是,如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩)

5.4 优化代码运行性能

  • 使用Code Split对代码进行分割成多个js文件,从而使单个文件体积更小,并行加载js速度更快,并通过import动态导入语法进行按需加载,从而达到需要使用时才加载该资源,不用时不加载资源
  • 使用Preload / Prefetch对代码进行提前加载,等未来需要使用时就能直接使用,从而用户体验更好
  • 使用Network Cache能对输出资源文件进行更好的命名,来好做缓存,从而用户体验更好
  • 使用Core-jsjs进行兼容性处理,让我们代码能运行在低版本浏览器
  • 使用PWA能让代码离线也能访问,从而提升用户体验

这篇文章转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: https://www.swvq.com/boutique/detail/tanhfcjhgc
  • 联系方式: luke.wu●vfv.cc
系列文章
更多 icon
同类精品
更多 icon
我要评论
我的头像
精彩评论
继续加载