zoukankan      html  css  js  c++  java
  • VUE发版白屏问题解决

    表现:F12检查发现资源404错误,一般是app.{hash}.css,    app.{hash}.js    等文件404。
    原因:发版后静态资源名称变了,但浏览器缓存了旧的index.html,引用的还是旧的资源url,资源404,所以白屏了。
    问题解决思路:在index.html写JS去检查是否资源载入出错,如果是则自动刷新。
    
    因为script标签是编译后插入的,没法用onerror事件去检查,因此另想方案。
    既然是script标签,那document.getElementByTagName是可以取到的。注意这在调试模式下不可行,因为调试模式只有一个app.js。
    function    checkUpgrade(){
    var scriptEls = document.getElementsByTagName("script");
    }
    这样就可以取到script标签了,但是为什么只有一个?
    这一个就是写的这段代码自身,因为浏览器js执行是单线程的,index.html刚解析和执行到这里,后面插入的script标签此时没有解析,因此我们需要判断一下延迟去处理。
    if (scriptEls.length < 3) {
              return setTimeout(checkUpgrade, 1000);
    }
    接下来就是构造一个ajax请求去检测文件是否存在了,因为我们不需要单独再去下载一次文件,这里用head请求就好了。
    遍历scriptEls,判断是否是我们要检测的目标
    if (element.src && element.src.indexOf('./static/js/')) {}
    如果是就执行检测
    var httpRequest_ = createHttpRequest();
    httpRequest_.open("HEAD", element.src, true);
    httpRequest_.send(null);
    可以写个函数来看返回的结果
    function handleResponse(xhr, callback) {
              xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {
                  callback(xhr.status);
                }
              };
              xhr.onerror = function () {
              }
            }
    好了,基本上表明清楚了。
    var httpRequest_ = createHttpRequest();
    httpRequest_.open("HEAD", element.src, true);
    httpRequest_.send(null);
    handleResponse(httpRequest_, function (status) {
                    if (breakNext)
                      return;
                    if (status == 404) {
                      //这是版本已经更新了,客户端缓存的旧版
                      //刷新就好了
                      fixedLoadingText = '正在刷新,请稍后';
                      breakNext = 1;
                      setTimeout(() => location.reload(), 3 * 1000);
                    }
    }
    用breakNext是因为我们检测一个就好了,避免发很多不必要的请求
    用fixedLoadingText是因为我做了一个加载动画,这个动画会在app.vue$mounted事件执行完毕后,也就是vue实例基本渲染好之后隐藏。
    
    当然除了404,我们还可以做更多检测
    if (status == 502 || status == 504 || status == 500) {
                      //这是后端无响应,提示正在升级
                      //10秒后继续检测是否升级完成
                      fixedLoadingText = '正在升级,请稍后';
                      breakNext = 1;
                      setTimeout(checkUpgrade, 5 * 1000);
                    }
                    else if (status == 0) {
                      //这是网络错误
                      // 一会儿再试
                      fixedLoadingText = '网络故障,正在重试';
                      breakNext = 1;
                      setTimeout(checkUpgrade, 10 * 1000);
                    }
    
    奉上全部代码
    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset=utf-8>
      <meta name=viewport content="width=device-width,initial-scale=1">
      <link rel="shortcut icon" type=image/x-icon href=static/favicon.ico>
      <title>VueApplication</title>
      <style media=screen type=text/css>
    
        #appLoading {
           100%;
          height: 100%;
          vertical-align: middle;
        }
    
        #appLoading p {
          background-color: white;
          border: 2px solid #ccc;
          text-align: center;
          position: absolute;
          display: block;
           400px;
          font-size: 20px;
          padding: 20px;
          top: 50%;
          left: 50%;
          -webkit-transform: translateY(-50%) translateX(-50%);
          transform: translateY(-50%) translateX(-50%);
        }
      </style>
    </head>
    
    <body>
      <div id=appLoading><span></span></div>
      <div id=app style="display: none"></div>
      <script>
        (function () {
    
          var dot = 1;
          var dots = { 0: '', 1: '.', 2: '..', 3: '...' };
          var checkError = true;
          var loadingText = '载入中';
          var fixedLoadingText = '';
          var loadStart = new Date();
          var percent = 0;
          var breakNext = false;
          var vueAppLoad = function (loadEnd) {
            var loadElapsed = loadEnd - loadStart;
            console.log("loadElapsed", loadElapsed / 1000, 's');
    
            //关闭载入提示,显示app界面
            document.getElementById("appLoading").style.display = "none";
            document.getElementById("app").style.display = "block";
          }
          var updateLoadingText = function (loadingText) {
            document.getElementById("appLoading").innerHTML = '<p><span style="float:left">' + loadingText + '</span><span style="float:right">' + percent + '%</span></p>';
          }
          var itv =
            setInterval(() => {
              dot = dot == 4 ? 0 : dot;
              var loading = document.getElementById("appLoading");
              var loadEnd = new Date();
              if (percent == 100) {
                clearInterval(itv);
                checkError = false;
                vueAppLoad(loadEnd);
                return;
              }
              var loadSeconds = (loadEnd - loadStart) / 1000;
              if (!fixedLoadingText) {
                if (loadSeconds < 5) {
                  percent = parseInt(loadSeconds * 5);
                }
                if (loadSeconds > 10) {
                  loadingText = '请稍后'
                  percent = 50;
                }
                if (loadSeconds > 20) {
                  loadingText = '努力载入'
                  percent = 60
                }
                if (loadSeconds > 30) {
                  loadingText = '网有点慢'
                  percent = 70;
                }
                if (loadSeconds > 40) {
                  fixedLoadingText = '请检查网络'
                  percent = 80;
                }
              }
              //检查app.vue事件创建的属性
              if (window.appCreated) {
                percent = 90;
              }
              if (window.appMounted) {
                percent = 100;
              }
              updateLoadingText((fixedLoadingText || loadingText) + dots[dot]);
              dot++;
            }, 450);
          function checkUpgrade() {
            //reset vars
            breakNext = false;
            function createHttpRequest() {
              if (window.ActiveXObject) {
                return new ActiveXObject("Microsoft.XMLHTTP");
              }
              else if (window.XMLHttpRequest) {
                return new XMLHttpRequest();
              }
            }
            function handleResponse(xhr, callback) {
              xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {
                  callback(xhr.status);
                }
              };
              xhr.onerror = function () {
              }
            }
            //版本更新后js和css的文件名称将更改,但因为客户端缓存了旧版的index.html页面
            //因此载入静态资源会404,表现为白屏
            //通过head请求去检测是否404来确认是否版本已更新,如果版本已更新则刷新页面
            var scriptEls = document.getElementsByTagName("script");
            if (scriptEls.length < 3) {
              return setTimeout(checkUpgrade, 1000);
            }
            for (let i = 0; i < scriptEls.length; i++) {
              if (breakNext)
                break;
              const element = scriptEls[i];
              if (element.src && element.src.indexOf('./static/js/')) {
                var httpRequest_ = createHttpRequest();
                try {
                  httpRequest_.open("HEAD", element.src, true);
                  httpRequest_.send(null);
                  handleResponse(httpRequest_, function (status) {
                    if (breakNext)
                      return;
                    if (status == 404) {
                      //这是版本已经更新了,客户端缓存的旧版
                      //刷新就好了
                      fixedLoadingText = '正在刷新,请稍后';
                      breakNext = 1;
                      setTimeout(() => location.reload(), 3 * 1000);
                    }
                    else if (status == 502 || status == 504 || status == 500) {
                      //这是后端无响应,提示正在升级
                      //10秒后继续检测是否升级完成
                      fixedLoadingText = '正在升级,请稍后';
                      breakNext = 1;
                      setTimeout(checkUpgrade, 5 * 1000);
                    }
                    else if (status == 0) {
                      //这是网络错误
                      // 一会儿再试
                      fixedLoadingText = '网络故障,正在重试';
                      breakNext = 1;
                      setTimeout(checkUpgrade, 10 * 1000);
                    }
                    else {
                      //其他情况一般无需处理了
                    }
                  });
                }
                catch (ex) {
                  console.log(ex);
                }
              }
            }
          }
          checkUpgrade();
        })();
      </script>
    </body>
    </html>
    
    在app.vue的mounted事件中做个标记,这样index.html就可以用这个标记去检查vue是否渲染好了。
    mounted() {
        window.appMounted = new Date(); //通知index.html
      },
    

      

  • 相关阅读:
    双日历时间段选择控件—daterangepicker(汉化版)
    vue elementui table表格展开行每次只展开一行
    vue-pdf PDF文件预览
    async await
    vuex发送axios请求
    jq调用浏览器下载文件 window.open()
    禁止页面右键、选择、F12操作
    vue 点击一条消息跳转相应的页面且对应相应的大模块和办理状态
    vue-infinite-scroll 滚动加载下一页
    填写流程当前登录人可以填写除自己可填项外还可看到他前面经办人相关填写的内容,且经办人后面的不可见
  • 原文地址:https://www.cnblogs.com/mrtiny/p/15675183.html
Copyright © 2011-2022 走看看