前端覆盖式发布引发的使用体验提升
我们公司前端采用的是覆盖式发布,过程就是:每次上线时构建好前端项目,将构建产物丢给运维,运维直接用新构建直接替换掉线上的版本(一般小作坊应该都是这么搞的...为啥这么搞?我想理由应该是想同的,在此我就不过多解读了)简单粗暴的背后隐患是很大的:假设这么个场景,有用户a和用户b有在使用我们的平台,有一天某时某刻,a正在使用我们的平台,成功登陆后,开始操作balabala,然后a没有关浏览器窗口,也没有关电脑,然后出去嗨了...在a嗨的同时,我们公司可爱的前端同学修复了一个bug,测试好了之后上线了一版,恰巧上线完成之后,b用户也来访问我们的平台,跟a一样的操作,没有出什么问题,但是a这时候继续使用我们平台的时候,问题就来了,发现点击好多页面没有反应,万幸的是a会武术,a打开f12后发现好多js文件404了?什么 ,我嗨之前不是好好的吗?怎么嗨完回来就gg了...
分析
仔细详细详细,这种场景下,用户a的问题是不是偶发还是必然?当然是必然了。。。不要给方案上的缺陷找借口了。。。现在前端开发一般是webpack构建项目,在html只会写一个入口的js文件,多个页面的js文件都会被webapck切分代码在配置中映射起来,通过webpack.require来进行懒加载。比方说用户页面和设置页面,用户访问用户页面(没有打开设置页面),这个时候我们修改设置页面并更新一个新版本,用户在打开设置页面的时候就会出现js404:入口js文件记录的设置页面还是修改前的js哈希文件,我们上线后会覆盖掉之前的文件,因此用户就会报js404错误。。。
解决
发布方案估计在很长一段时间没得改了。。。那就只能在现有基础上改了。。。在网上查找了解到有人使用 addEventListener 这种方法,第三个参数必须为true,使用捕获传播(因为是监听资源的错误),如下
window.addEventListener("error", handler, true)
思路就是,如果监听到错误,判断是否为js错误,然后获取js的src值,进行一次HEAD请求,确认是否http返回状态码为404,如果是404的话提示用户更新版本(刷新页面)后再使用:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <script> /** * 判断js资源是否返回404状态码 * @param url * @return {Promise<any>} */ function isScript404(url) { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 404) { resolve(true) return } resolve(false) } xhr.send() }) } /** * 处理错误函数 * @param e */ async function handleError(e) { console.log('script resource load error....') let script = e.target let SCRIPT = 'SCRIPT' let tagName = script.tagName let src = script.src // js资源网络请求失败 if (SCRIPT === tagName) { let bool = await isScript404(src) if (!bool) { return } console.log('更新版本,请刷新页面') } } // 绑定事件 window.addEventListener('error', handleError, true) </script> <script src="b.js"></script> </body> </html>