前言
在前端项目的部署过程中,我们经常会遇到一些问题,例如:如何在部署新版本时,避免影响到正在使用的用户?如何实现前端资源的无感升级?本文将通过 Ant Design Pro 项目为例,介绍如何将项目部署到腾讯云 COS,并修改 index.html 绑定的 JS 和 CSS 地址,从而实现前端资源的无感升级。
方案
在前端开发中,HTML、JS和CSS是三个主要组成部分。为了实现前端资源的无感升级,我们需要在更新JS和CSS资源时,确保用户能够在刷新页面后立即获取到最新的HTML,并渲染更新后的页面。所以方案很清晰:
- 使用版本控制策略:
- 在更新JS和CSS资源时,可以通过添加hash值来区分不同的版本。这样,每次更新资源时,文件名会发生变化,从而避免浏览器缓存导致用户无法获取到最新资源的问题
- 动态更新HTML资源链接:
- 在更新JS和CSS资源后,需要同步更新HTML中绑定的资源链接
- 利用浏览器缓存策略,合理设置HTTP缓存策略:
- HTML不设置缓存:为了让用户每次刷新页面都能拿到最新的HTML
- JS和CSS设置缓存:与HTML不同,我们希望浏览器能够缓存JS和CSS资源,以提高页面加载速度
大白话,就是通过切换index.html
中绑定的资源,来实现无感升级。接下来我们具体操作一下
准备工作
首先,我们需要准备以下工具和资源:
- 一个已经创建好的 Ant Design Pro 项目
- 腾讯云账号,并开通 COS 服务
- Serverless Framework
步骤一:部署到腾讯云 COS
-
登录腾讯云控制台,进入 COS 管理页面,创建一个新的存储桶,设置存储桶的权限为公有读。
-
将存储桶设置成
静态网站
,这样可以直接通过域名访问 COS 存储桶目录下的index.html 文件 -
在 Ant Design Pro 项目根目录下新建
deploy/index.ts
文件,用于编写部署脚本:
typescript
注意:安全起见,COS 的 SecretId/SecretKey 不能保存到前端代码中
// 关键代码
import COS from 'cos-js-sdk-v5';
import compressing from 'compressing';
const upload = (data, key) => {
cos.uploadFile({
Bucket: 'examplebucket-1250000000', /* 填入您自己的存储桶,必须字段 */
Region: 'COS_REGION', /* 存储桶所在地域,例如ap-beijing,必须字段 */
Key: 'dist.zip', /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
Body: data, /* 必须,上传文件对象,可以是input[type="file"]标签选择本地文件后得到的file对象 */
onFileFinish: function (err, data, options) { /* 非必须 */
console.log(options.Key + '上传' + (err ? '失败' : '完成'));
},
}, function(err, data) {
console.log(err || data);
});
}
// 执行文件压缩
compressing.zip
.compressDir(distPath, zpiPath)
.then(() => {
console.log('压缩成功,准备上传');
const data = fs.readFileSync(zpiPath);
upload(data, 'dist.zip')
})
.catch((error) => {
console.log('压缩失败', error);
});
压缩打包文件目的是为了方便传输和存档,在处理回滚操作时,我们只需要解压对应的压缩包,就可以获取到回滚资源了
-
COS 中有很多方便的工具可以用,有现成的工具可以对上传到 COS 中的 zip 文件进行解压,直接开启工具就好。利用 SCF 对 COS 中的 ZIP 文件进行解压。具体安装方法请参考官方文档:cloud.tencent.com/document/pr...
-
在 Ant Design Pro 项目根目录下,运行
npm run build
命令,将项目打包成静态资源。 -
在 package.json 中新建一条命令
"deploy": "npx tsx ./deploy/index.ts"
,并执行npm run deploy
, 将静态资源 ZIP 包上传到 COS 存储桶。
至此,我们已经将 Ant Design Pro 项目部署到了腾讯云 COS,接下来我们需要配置 Hash 路由,以及修改 index.html 绑定的 JS 和 CSS 地址。
步骤二:修改打包配置
为了实现
版本
的功能,我们还需要对配置文件进行一定的修改
- 在 Ant Design Pro 项目的
config.ts
文件中,设置打包配置:
javascript
export default defineConfig({
// 若不开启 hash 路由,在访问 COS 资源后,刷新浏览器会出现找不到资源的问题
history: { type: 'hash' },
/**
* @name 开启 hash 模式
* @description 让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。
* @doc https://umijs.org/docs/api/config#hash
*/
hash: true,
/**
* 开启后,会生成 asset-manifest.json 文件,记录了打包后的资源名
*/
manifest: {
basePath: './'
},
...
});
- 动态修改打包配置,确定打包后的访问路径
ini
// 确定压缩包的名称
const uuid = uuidv4();
const afterDistName = `${uuid}-dist`;
const distPath = path.resolve(__dirname, `../../${afterDistName}`);
// 改写 config ,打包
export const onChangeConfig = (afterDistName = '') => {
const configPath = path.resolve(__dirname, '../../config/config.ts');
const configString = fs.readFileSync(configPath, { encoding: 'utf-8' });
const old = 'export default defineConfig({';
const now = `export default defineConfig({publicPath:'/${afterDistName}/',`;
/* 每次打包都会生成新的 publicPath,这就相当于 `版本号` */
// 改 config 中的 publicPath
const newStr = configString.replace(old, now);
fs.writeFileSync(configPath, newStr);
};
结合步骤一
,就可以将带有版本号的 dist 资源上传到 COS
步骤三:修改 index.html 绑定的 JS 和 CSS 地址
- 更新 index.html 文件中绑定的 JS、CSS 资源地址,并将新版的 index.html 上传到 COS
javascript
// distPath | afterDistName:步骤二中获取
// 更新index.html
const rewriteIndexHtml = (time = '') => {
// 替换 html
const htmlPath = `${distPath}/index.html`;
const content = fs.readFileSync(htmlPath, { encoding: 'utf-8' });
const jsonData = require(`${distPath}/asset-manifest.json`)
const js = jsonData['./umi.js']
const css = jsonData['./umi.css']
// 更新 index.html 中绑定的资源
const newContent = content.replace(js, `/${afterDistName}${js}`).replace(css, `/${afterDistName}${css}`)
// 步骤一 中上传 COS 的代码
// 将新的 index.html 上传到 COS,替换旧版本的 index.html
upload(newContent, 'index.html)
}
步骤四
处理资源缓存,这里只需要处理 index.html
的缓存,进入 COS 对应的存储桶,点击编辑 index.html
:
json
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0'
'Expires': '0'
最终 COS 中的文件结构:
至此,我们已经修改了 index.html 绑定的 JS 和 CSS 地址,实现了前端资源的无感升级。当我们需要更新项目时,只需重新打包并上传静态资源到 COS 存储桶,用户刷新页面,就可以看到最新的内容。
前端部署,只需要执行npm run build && npm run deploy
彩蛋
以上所有的步骤都是通过前端 node 脚本实现,如果需要实现版本回滚的功能,就需要借助 Serverless
和数据库的能力(数据库也只是用于记录 afterDistName
值),我们的回滚功能只要读取历史 afterDistName
的值,然后重复 步骤三
即可
js
/** Serverless 中的伪代码 **/
// 模板页面
function readIndexHtml() {
return `<html><head><meta charset="utf-8" /><title>Serverless COS Demo</title></head><body><h1>Serverless COS Demo</h1><select id="js-selector">{optioins}</select><script>
document.getElementById('js-selector').addEventListener('change', function () {
const newJsUrl = this.value;
// 此处可以换成 【步骤三】 的逻辑
alert(newJsUrl)
});
</script></body></html>`
}
exports.main = async (event, context) => {
try {
// 获取切换版本的 UI 代码,用于浏览器渲染
const indexHtml = readIndexHtml();
// 这里使用 mock 数据。实际业务中,应该从数据库中,获取当前所有版本
const versions = ['js_url1', 'js_url2', 'js_url3']
const versionStr = versions.map(item => `<option value="${item}">${item}</option>`).join('')
// 重组 html
const newIndexHtml = indexHtml.replace('{options}', versionStr)
return {
isBase64Encoded: false,
statusCode: 200,
headers: { 'Content-Type': 'text/html' },
body: newIndexHtml,
};
} catch (err) {
return {
isBase64Encoded: false,
statusCode: 500,
headers: { 'Content-Type': 'text/plain' },
body: 'Error',
};
}
};
需要开通外网 HTTP 访问 Serverless 函数的服务
这样我们就可以通过访问 Serverless 进行版本切换
总结
本文以 Ant Design Pro 项目为例,介绍了如何将项目部署到腾讯云 COS,并结合 Serverless 的能力,实现前端资源的无感升级。通过这种方式,我们可以在项目更新时,让用户无感的升级到最新的版本,提升用户体验。
需要注意的是,这种方式适合个人项目。如果需要更加安全和高效的方式,还是需要结合后台数据库和接口实现