转自: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 就能注册成功了。