likes
comments
collection
share

vue项目部署自动检测更新

作者站长头像
站长
· 阅读数 11

前言

当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。

在以往解决方案中,不少人会使用websocket去通知客户端更新,但是为了这么个小功能加入websocket是十分不明智的,新方案的思路是去轮询请求index.html文件,从中解析里面的js文件,由于vue打包后每个js文件都有指纹标识,因此可以对比每次打包后的指纹,分析文件是否存在变动,如果有变动即可提示用户更新

原理

vue项目部署自动检测更新

封装函数 auto-update.js

let lastSrcs; //上一次获取到的script地址

const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm;

async function extractNewScripts() {
	const html = await fetch('/standard?_timestamp=' + Date.now()).then((resp) => resp.text());
	scriptReg.lastIndex = 0;
	let result = [];
	let match;
	while ((match = scriptReg.exec(html))) {
		result.push(match.groups.src)
	}
	return result;
}

async function needUpdate() {
	const newScripts = await extractNewScripts();
	if (!lastSrcs) {
		lastSrcs = newScripts;
		return false;
	}
	let result = false;
	if (lastSrcs.length !== newScripts.length) {
		result = true;
	}
	for (let i = 0; i < lastSrcs.length; i++) {
		if (lastSrcs[i] !== newScripts[i]) {
			result = true;
			break
		}
	}
	lastSrcs = newScripts;
	return result;
}
const DURATION = 5000;

function autoRefresh() {
	setTimeout(async () => {
		const willUpdate = await needUpdate();
		if (willUpdate) {
			const result = confirm("页面有更新,请刷新查看");
			if (result) {
			  location.reload();
			}
		}
		autoRefresh();
	}, DURATION)
}
autoRefresh();

引入

在main.js中引入

// 引入自动更新提醒
import "@/utils/auto-update.js"

使用element-ui的notify提示更新

修改auto-update.js


let lastSrcs; //上一次获取到的script地址

const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm;

async function extractNewScripts() {
	const html = await fetch('/standard?_timestamp=' + Date.now()).then((resp) => resp.text());
	scriptReg.lastIndex = 0;
	let result = [];
	let match;
	while ((match = scriptReg.exec(html))) {
		result.push(match.groups.src)
	}
	return result;
}

async function needUpdate() {
	const newScripts = await extractNewScripts();
	if (!lastSrcs) {
		lastSrcs = newScripts;
		return false;
	}
	let result = false;
	if (lastSrcs.length !== newScripts.length) {
		result = true;
	}
	for (let i = 0; i < lastSrcs.length; i++) {
		if (lastSrcs[i] !== newScripts[i]) {
			result = true;
			break
		}
	}
	lastSrcs = newScripts;
	return result;
}
const DURATION = 5000;

function autoRefresh() {
	setTimeout(async () => {
		const willUpdate = await needUpdate();
		if (willUpdate) {
			// 延时更新,防止部署未完成用户就刷新空白
			setTimeout(()=>{
				window.dispatchEvent(
					new CustomEvent("onmessageUpdate", {
						detail: {
							msg: "页面有更新,是否刷新?",
						},
					})
				);
			},30000);
		}
		autoRefresh();
	}, DURATION)
}
autoRefresh();

编写模板

CnNotify.vue文件

<template>
  <div class="cn_notify">
    <div class="content">
      <i class="el-icon-message-solid"></i>
      {{ msg }}
    </div>
    <el-row :gutter="20">
      <el-col :span="7" :offset="10">
        <el-button type="primary" @click="onSubmit">确定</el-button>
      </el-col>
      <el-col :span="7">
        <el-button @click="cancle">取消</el-button>
      </el-col>
    </el-row>
  </div>
</template>
<script>
export default {
  props: {
    msg: {
      type: String,
      default: "",
    },
  },
  data() {
    return {};
  },
  created() {},
  methods: {
    // 点击确定更新
    onSubmit() {
      location.reload();
    },
    // 关闭
    cancle() {
      this.$parent.close();
    },
  },
};
</script>
<style lang='less' scoped>
.cn_notify {
  .content {
    padding: 20px 0;
  }
  .footer {
    display: flex;
    flex-direction: row-reverse;
  }
}
</style>

使用

App.vue

// 引入
import CnNotify from "@common/components/CnNotify.vue";
components: {
  CnNotify,
},
mounted() {
    this.watchUpdate();
},
methods: {
    watchUpdate() {
      window.addEventListener("onmessageUpdate", (res) => {
        let msg = res.detail.msg;
        this.$notify({
          title: "提示",
          duration: 0,
          position: "bottom-right",
          dangerouslyUseHTMLString: true,
          message: this.$createElement("CnNotify", {
            // 使用自定义组件
            ref: "CnNotify",
            props: {
              msg: msg,
            },
          }),
        });
      });
    },
},