实现页面版本自动刷新--vite插件今天在工作的时候,发现了公司的运营活动h5项目好像从来都没有缓存,很好奇,发现在项目
先看效果
发现没有 ?就是访问index.html
会被重定向到171763620.html
,看到这里是不是一脸懵逼,为什么要重定向?
主要原因是为了解决当你部署前端页面出现缓存的情况。
前情
今天在工作的时候,发现了公司的运营活动h5项目好像从来都没有缓存,很好奇,发现在项目每次打包的时候,都会出现一个随机的html并且和index的html内容一致
于是我就去查找相关逻辑,发现每次页面加载的时候都会执行一个脚本,大概内容如下
// ver.js
function getVersion(){return"1717555412"}
// first-script.js
var latestPage = getVersion() + '.html';
var pathnameArr = location.pathname.split('/');
var len = pathnameArr.length;
var currentPage = pathnameArr[len - 1];
var cache = localStorage.getItem(cacheName);
var isIndex = currentPage === 'index.html';
if (currentPage && isIndex && (!cache || cache === latestPage)) {
localStorage.setItem(cacheName, latestPage);
} else if (currentPage && (latestPage !== currentPage)) {
var dirctUrl = location.href.replace(currentPage, latestPage);
window.location.replace(dirctUrl);
}
大致逻辑就是每次项目打包的时候会生成一个ver.js
文件 用来存储当前的版本号,然后复制一份index.html
重命名为版本号,在每次进入页面的时候会去判断url上如果是index.html
则会缓存该版本号,反之则会获取ver.js
的版本号对比缓存中的版本号是否一致,不一致则会重定向最新的html。
加上因最近开发的项目都是vite
相关的,于是乎想着开发一个vite
插件可以实现上面的功能。
vite插件
我们来看看怎么开发vite插件
,发现官网有相关文档可以参考学习。
开发的关键主要是需要使用哪些钩子,我们先来学习一下,主要分为 通用钩子 和 Vite独有钩子
通用钩子
Vite 是基于 Rollup 这个构建工具来封装的,所以 Vite 中的一部分钩子其实就是Rollup 中的钩子,更多详细钩子使用可以查看官网
钩子 | 描述 |
---|---|
options | 构建阶段的第一个钩子,在服务器启动时被调用 |
buildStart | 构建阶段的第二个钩子,在服务器启动时被调用 |
resolveId | 主要用于自定义模块解析的行为 |
load | 用于自定义模块加载逻辑 |
transform | 用于修改模块的源代码,可以在构建期间对模块进行转换和处理 |
buildEnd | 在服务器关闭时被调用 |
closeBundle | 在 Vite 打包生成 bundle 文件时触发 |
Vite独有钩子
钩子 | 描述 |
---|---|
config | 在解析 Vite 配置前调用,钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量 |
configResolved | 在解析 Vite 配置后调用。使用这个钩子读取和存储最终解析的配置。当插件需要根据运行的命令做一些不同的事情时,它也很有用。 |
configureServer | 是用于配置开发服务器的钩子。最常见的用例是在内部connect应用程序中添加自定义中间件: |
configurePreviewServer | 与 configureServer 相同,但用于预览服务器。 |
transformIndexHtml | 允许你在构建过程中修改生成的 HTML 文件。这个钩子函数在生成最终的 index.html 文件之前执行,允许你自定义 HTML 内容或添加额外的标签、脚本等。 |
handleHotUpdate | 执行自定义 HMR 更新处理。钩子接收一个带有以下签名的上下文对象: |
以我们现在需要实现的功能的插件应该在输出阶段的时候,写入版本判断逻辑,然后复制html
在官网中找到了generateBundle
钩子,
generateBundle
是在 Vite 中通常用于在构建过程结束时对生成的 bundle 进行最后的处理;
我们就可以在generateBundle
钩子中创建 ver.js
写入版本号,在transformIndexHtml
插入版本逻辑,在输出生成阶段的最后时刻 writebundle
中执行复制操作
具体代码如下
export default function refreshPlugin (packPath, projectName = 'refreshProject'): Plugin {
// 生成的版本号
let version: null | number = null;
// 打包路径
const distPath = packPath;
// html路径
const indexPath = path.join(distPath, 'index.html');
// 版本号js文件路径
const verFilePath = path.join(distPath, 'ver.js');
return {
name: 'vite:refresh',
generateBundle() {
// 生成版本号文件
version = Math.round(+new Date()/1000)
const writeFileSync = (version) => {
fs.writeFileSync(verFilePath,
`function getVersion() { return '${version}' }`, 'utf8')
}
if (!fs.existsSync(distPath)) {
fs.mkdirSync(distPath);
}
writeFileSync(version);
},
transformIndexHtml(html) {
// 插入版本判断刷新逻辑
const insertIndex = html.lastIndexOf('</title>') + 8 // 找到html中title闭合标签的位置
const insertContent = `
<script src="./ver.js?t=${+new Date()}"></script>
<script>
(() => {
const version = getVersion();
const latestPage = version + '.html';
const pathnameArr = location.pathname.split('/');
const len = pathnameArr.length;
const currentPage = pathnameArr[len - 1];
const cache = localStorage.getItem('${projectName}');
const isIndex = currentPage === 'index.html';
if (currentPage && isIndex && (!cache || cache === latestPage)) {
localStorage.setItem('${projectName}', latestPage);
} else if (currentPage && (latestPage !== currentPage)) {
const dirctUrl = location.href.replace(currentPage, latestPage);
window.location.replace(dirctUrl);
}
})()
</script>
`
return html.slice(0, insertIndex) + insertContent + html.slice(insertIndex)
},
writeBundle() {
// 复制html
const verstr = fs.readFileSync(verFilePath, 'utf-8')
const version = (verstr.match(/\d/g) as RegExpExecArray).join('')
const versionedIndexPath = path.join(distPath, `${version}.html`);
fs.copyFileSync(indexPath, versionedIndexPath);
}
}
}
其中在title闭合标签的位置插入脚本,是为了让其页面加载的时候优先执行,因为title标签在head标签里,其脚本所在的位置会在页面内容(如 <body>
中的内容)被加载和渲染之前执行
vite-plugin-page-refresh插件写完之后就通过npm publish
发布
插件使用
import refreshPlugin from 'vite-plugin-page-refresh'
// vite.config.ts | vite.confing.js
export default defineConfig({
plugins: [
// path.resolve(process.cwd(), 'dist')代表当前项目的dist目录
refreshPlugin(path.resolve(process.cwd(), 'dist'))
]
})
转载自:https://juejin.cn/post/7376915846109675561