简介
在前端开发中,检测网站版本是否有更新是一个非常场景的需求。那么具体有哪些方式可以实现这一需求呢? 下面我就来分享一下我所接触到的方案
部署
部署一个react项目
在开始检测页面版本更新
需求之前,我们先使用docker+nginx
来部署一波react
项目。因为我们需要模拟线上的真实环境。
- 创建React项目
ts
npx create-react-app version-detect --template typescript
PS:这里我采用pnpm包管理器
,脚手架默认采用的是npm包管理器
此时通过pnpm import
命令将npm项目转为pnpm项目
- 编写
dockerFile
文件用于构建镜像 首先来看一下docker的结构图,如下图可以看出宿主机
与docker
项目之间的依赖关系。那么接下来就来编写dockerFile
文件
-
在项目的
根目录
创建.dockerignore
文件用指定要忽略的文件和目录.git
node_modules -
在项目的
根目录
创建Dockerfile
文件用编写指定image
的代码。指定镜像分为如下两个步骤
步骤一: (构建前端静态的资源)
bash
# 将node节点命名为builder
FROM node:17-alpine AS builder
# 指定工作目录
WORKDIR /app
# 将当前目录下的所有文件复制到工作目录当中。
COPY . ./
# 安装pnpm包管理器
RUN npm install -g pnpm
# 安装node_modules并且进行打包
RUN pnpm install && pnpm build
PS: FROM ...(基础镜像) AS ...(别名)就是给这个阶段的镜像起个别名,在后面引用这个阶段的镜像时直接使用别名
步骤二: (构建ngnix服务)
ts
# 基于nginx镜像构建
FROM nginx:alpine
# 将工作目录设置为nginx静态资源目录
WORKDIR /usr/share/nginx/html
# 删除之前存在的默认文件
RUN rm -rf ./*
# 从builder中将/app/build复制到当前目录里
COPY --from=builder /app/build .
# 全局运行nginx并且关闭进程守护
ENTRYPOINT ["nginx", "-g", "daemon off;"]
最终代码如下:
- 在项目中执行
docker build ~
构建镜像我们的镜像.如果你的Dockerfile
erlang
docker build -t version-detect-nginx .
构建完成之后,就可以通过docker images
命令或者用可视化桌面Docker Desktop
看到构建的镜像了
- 最后通过
docker run ~
来运行此项目,
ts
# docker run 是指通过一个镜像启动一个容器,-p是一个端口映射,容器里面Nginx默认为80, 8080是指定宿主机端口, -d就是后台运行。--name version-detect-project就是定义容器的名字,version-detect-nginx就是指定镜像的名字
docker run -p 8080:80 -d --name version-detect-project version-detect-nginx
Docker Desktop
的用法 最终就可以通过http://localhost:8080/
来访问部署好的react
项目了
版本检测
通过上面的步骤,我们完成了基于docker+nginx
的react项目部署,接下来我们就来实现版本更新检测
的功能
唯一hash值
- hash文件的生成
创建hash.js
文件,生成一个唯一值
用于标识当前版本
ts
const crypto = require('crypto');
const fs = require('fs');
const data = crypto.randomBytes(16).toString('hex');
const hash = crypto.createHash('sha256').update(data).digest('hex');
fs.writeFile('./build/hash.txt', hash, (err) => {
if (err) throw err;
});
修改package.json
下的build
命令
json
"build": "react-scripts build && node ./hash.js",
运行build
命令之后就会生成hash.text
文件 2. 在react项目添加轮训
,请求hash.text
文件。每隔1s
请求一次。
ts
const oldHash = useRef(null)
useEffect(() => {
loopTimer();
}, [])
// 使用setTimeout模拟setInterval
function loopTimer() {
let timer: any = null;
const interval = async () => {
try {
const { data } = await axios.get('/hash.txt')
if (oldHash.current && oldHash.current !== data) {
console.log('检测到版本更新了');
} else {
oldHash.current = data
}
} finally {
timer = setTimeout(interval, 1000);
}
};
setTimeout(interval, 1000);
return {
cancel: () => {
clearTimeout(timer);
},
};
}
重新打包react项目,然后重新部署。可以看到控制台打印了检测到版本更新了
字段。然后我们就可以愉快的提示用户刷新页面了
通过HEAD请求
我们也可以通过轮训获取到当前项目协商缓存字段Etag
来判断版本是否有更新。因为我们不需要获取到响应体
只需要响应头的Etag
数据即可,因此采用HEAD
方法来实现
ts
// 使用setTimeout模拟setInterval
function loopTimer() {
let timer: any = null;
const interval = async () => {
try {
const { headers } = await axios.head('')
if (oldEtag.current && oldEtag.current !== headers.Etag) {
console.log('检测到版本更新了');
} else {
oldEtag.current = headers.Etag
}
} finally {
timer = setTimeout(interval, 1000);
}
};
setTimeout(interval, 1000);
return {
cancel: () => {
clearTimeout(timer);
},
};
}
更新react项目重新部署之后etag
发生了变化,因此就可以判定版本更新了
其他
当然你也可以使用SSE方案由后端主动告知前端版本更新
等,本来想用nestJS实现一下的但时间有限,而且也不难可以参考神光
大佬的实现方式
PS: 如果公司所有项目都需要检测项目版本是否更新
可以编写webpack插件,让插件去注入轮询代码,这样子就不需要自己手动的一个个项目去编写注入了