【转载】Webpack高级(四)——提高代码运行性能

转自:https://blog.csdn.net/u010358168/article/details/124932198

Preload

我们前面已经做了代码分割,同时会使用 import 动态导入语法来进行代码按需加载(我们也叫懒加载,比如路由懒加载就是这样实现的)。
但是加载速度还不够好,比如:是用户点击按钮时才加载这个资源的,如果资源体积很大,那么用户会感觉到明显卡顿效果。
我们想在浏览器空闲时间,加载后续需要使用的资源。我们就需要用上 Preload 或 Prefetch 技术。

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

    共同点:

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

区别:

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

问题

兼容性较差。
可以去 Can I Use 网站查询 API 的兼容性问题,Preload 相对于 Prefetch 兼容性好一点。

使用

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

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

Network Cache

将来开发时我们对静态资源会使用缓存来优化,这样浏览器第二次请求资源就能读取缓存了,速度很快。
但是这样的话就会有一个问题, 因为前后输出的文件名是一样的,都叫 main.js,一旦将来发布新版本,因为文件名没有变化导致浏览器会直接读取缓存,不会加载新资源,项目也就没法更新了。
所以我们从文件名入手,确保更新前后文件名不一样,这样就可以做缓存了。
它们都会生成一个唯一的 hash 值。

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

使用

修改config/webpack.prod.js和config/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",
    }),
]

问题

当我们修改 math.js 文件再重新打包的时候,因为 contenthash 原因,math.js 文件 hash 值发生了变化(这是正常的)。
但是 main.js 文件的 hash 值也发生了变化,这会导致 main.js 的缓存失效。
明明我们只修改 math.js, 为什么 main.js 也会变身变化呢?
原因:
更新前:math.xxx.js ⇒ main.js 引用的 math.xxx.js
更新后:math.yyy.js ⇒ main.js 引用的 math.yyy.js, 文件名发生了变化,间接导致 main.js 也发生了变化。

解决

将 hash 值单独保管在一个runtime文件中。
我们最终输出三个文件:main、math、runtime。当 math 文件发送变化,变化的是 math 和 runtime 文件,main 不变。
runtime 文件只保存文件的 hash 值和它们与文件关系,整个文件体积就比较小,所以变化重新请求的代价也小。
修改 config/webpack.prod.js 文件:

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

这样就解决了引用文件名变化导致缓存失效的问题了。

core-js

过去我们使用 babel 对 js 代码进行了兼容性处理,其中使用@babel/preset-env 智能预设来处理兼容性问题。
它能将 ES6 的一些语法进行编译转换,比如箭头函数、点点点运算符等。但是如果是 async 函数、promise 对象、数组的一些方法(includes)等,它没办法处理。
所以此时 js 代码仍然存在兼容性问题,一旦遇到低版本浏览器会直接报错。
core-js 是专门用来做 ES6 以及以上 API 的 polyfill。
polyfill 翻译过来叫做垫片/补丁,就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该新特性。

使用

npm i core-js

手动引入

main.js

import "core-js";

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

按需引入

import "core-js/es/promise";
只引入打包 promise 的 polyfill,打包体积更小。但是将来如果还想使用其他语法,还需要手动引入库很麻烦。

自动按需引入

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

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

PWA

开发 Web App 项目,项目一旦处于网络离线情况,就没法访问了。
我们希望给项目提供离线体验。
渐进式网络应用程序(progressive web application - PWA):是一种可以提供类似于 native app (原生应用程序) 体验的 Web App 的技术。
其中最重要的是,在离线(offline)时应用程序能够继续运行功能。
内部是通过 Service Workers 技术实现的。

使用

npm i workbox-webpack-plugin -D
修改config/webpack.prod.js

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

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);
      });
  });
}

此时如果直接通过 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

解决路径问题

npm i serve -g
serve 是用来启动开发服务器来部署代码查看效果的。
serve dist
此时通过 serve 启动的服务器,service-worker 就能注册成功了。

点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注