场景:
页面打开不操作,前端项目代码更新重新部署后(比如Jenkins发布部署)页面不刷新,操作页面(点击打开弹窗、切换菜单等),页面没有反应,控制台报错 Uncaught SyntaxError: Unexpected token <。这个问题偶现,只有在项目重新部署后会出现,页面刷新后就恢复正常
问题原因:
在前端项目未更新之前打开的页面,在前端项目更新后,hash码更新导致js请求路径改变,而页面依然以之前的路径请求js资源,必然会请求不到。如果资源请求不到一般会报404问题,但是服务器配置了404页面,对于请求不到的资源会返回一个404页面,在script标签里解析html文件,就会报错Unexpected token <
解决:
报错时给用户提示,用户点击确认后刷新页面;目前还没找到catch这种错误的方法,但是,可以模拟这种错误的出现。js文件是以script标签的形式动态添加到head标签里的,可以给head绑定DOMNodeInserted这个事件在有子元素插入的时候触发,可以在回调里拿到插入的标签名以及标签的属性包括src。这样在所有js资源加载时我们都可以在回调事件里拿到资源路径,然后在创建一个请求去请求该资源
代码:
在app.vue中,添加如下代码。对于报错的js文件,我们会在xhr.responseText获取到一个以尖括号开头的html,这时候我们就可以知道当前资源路径失效,就可以在此时做一些处理
<script> export default { name: "App", created() { const head = document.getElementsByTagName('head')[0]; head.addEventListener('DOMNodeInserted', e => { // 获取标签名 const type = e.target.tagName; // 获取资源路径 const url = e.target.src; if (type === 'SCRIPT' && url) { let xhr = new XMLHttpRequest(); xhr.open('get', url); xhr.onload = () => { const text = xhr.responseText; if (text.indexOf('<') === 0) { this.$confirm("检测到新版本已发布,刷新后加载最新内容!",'提示', { confirmButtonText: '刷新', type: 'error', closeOnClickModal: false, closeOnPressEscape:false, closeOnHashChange:false, showCancelButton: false, showClose: false }).then(res => { window.location.reload(true); }) } } xhr.send(); } }) } }; </script>
问题:使用如上代码可以解决Uncaught SyntaxError: Unexpected token <问题,但随之控制台出现下面错误;
DOMNodeInserted 是一个已经被废弃的 Mutation Events 接口,用于监听DOM树中节点的插入事件;推荐使用 MutationObserver 接口,MutationObserver性能更优
使用 MutationObserver 重写代码:
<script> export default { name: "App", created() { const head = document.getElementsByTagName('head')[0]; // 定义DOM树变化后的回调 const callBack = (mutationsList, observer) => { for (const mutation of mutationsList) { if (mutation.type === 'childList' && mutation.addedNodes && mutation.addedNodes[0] && mutation.addedNodes[0].tagName === "SCRIPT") { const url = mutation.addedNodes[0].src; console.log(url); if (url) { let xhr = new XMLHttpRequest(); xhr.open('get', url); xhr.onload = () => { const text = xhr.responseText; // console.log(text); if (text.indexOf('<') === 0) { this.$confirm("检测到新版本已发布,刷新后加载最新内容!",'提示', { confirmButtonText: '刷新', type: 'error', closeOnClickModal: false, closeOnPressEscape:false, closeOnHashChange:false, showCancelButton: false, showClose: false }).then(res => { window.location.reload(true); }) return false; } } xhr.send(); } } } } // 配置项 const config = {attributes: false, childList: true, subtree: false}; // observer观察器 const observer = new MutationObserver(callBack); // 开始观察 observer.observe(head, config); // 停止观察 // observer.disconnect(); } }; </script>
参考:
https://blog.csdn.net/FortheOne/article/details/123073336
https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver