VUE 路由切换白屏的问题

邱秋 • 2018年05月18日 • 阅读:8313 • vue

关于 vue 路由切换的白屏,事实上在开发的过程中,我一直没有遇到过。

我有个哥们遇到这个问题,问我怎么解决的, 我晕了,我没遇到这样的问题啊,我怎么解决啊啊啊啊。。

事实上是遇到过一回的。

以下内容于 2019-07-25 修改.

服务器部署配置问题
这个问题造成的白屏体现在:
首页可以正常浏览,但是------通过$router.push('/home')跳转页面 正常,然后刷新 就是白屏或 404

本不想在文章加入这个问题和解决方案,因为官网已经提供了正确的部署姿势,(Vue 的路由模式自行查看文档),主要是针对HTML5 History 模式:

const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

dev 和 build 都没有问题,这个是肯定的, 所以问题就出在 服务器配置上,以 nginx 为例, 正确的配置如下:

location / {
  ....
  try_files $uri $uri/ /index.html; #重点
}

Apache,原生 Node.js,IIS,Caddy,Firebase 主机,请查看 vur-router 后端配置例子

以上内容于 2019-07-25 修改.

以下内容于 2019-04-23 修改.

如果你是使用脚手架初始化环境,可以跳过这部分.如果自己配置webpack,可以接着看 确定做了如下配置

devServer: {
  ...
  contentBase: false, //必须
  historyApiFallback: true, //必须
  ...
},
entry: {
    app: ['./src/main.js'],
    vendors: ['vue', 'vue-router'] //注意这里
},
plugins: [
  ....
  new HtmlWebpackPlugin({
    ...
    chunks: ['vendors', 'app'], //注意这里,这里的chunks 要包含入口的 entry
  })
]

以上内容于 2019-04-23 修改.

场景一:IE9(兼容性问题)

严格来说不是白屏的问题,应该说是兼容性问题,直接就是不支持,报错了,渲染无法执行造成的。解决方式就是

npm i babel-polyfill -D

然后在入口 mian.js 引入就完事了,就着么简单

import "babel-polyfill";

或者

//webpack
entry: {
      app: ['babel-polyfill','./src/main.js'],
      vendors: ['vue', 'vue-router']
},

以下内容于 2019-04-23 修改.

假如真的是 js 兼容性问题, 那么真的只是 引入babel-polyfill 或者在webpack入口加入 babel-polyfill问题就能解决吗, 其实不一定的, 这个要看项目的使用情况. babel-polyfill 不是万能的. 那么如何排查兼容性问题呢(假如真的是兼容性问题), 因为我们在手机上直接调试,有些错误不会那么容易显而易见.我是这么调试的. (仅供参考!)

<template>
  <div>
    <!-- 错误直接显示在这里. 不用 alert() ,console.log()  -->
    {{error}} ...
    <!-- other element -->
  </div>
</template>
<script>
  export default{
    data{
      return {
        error:''
      }
    },
    moundted:{
      try{
        //一些请求数据的方法
      }catch(e){
        //这里抛出异常
        this.error = e
      }
    }
  }
</script>

这里要说的是有些兼容性问题是bable-polyfill搞不定的. 比如URLSearchParams

let data = new URLSearchParams();
for (var key in params) {
  data.append(key, params[key]);
}

那么肯定会报 URLSearchParams is not undefined,然后,错误只在一些低端的机型,一些偶然的场合出错.加大了错误的排查.
以下可以解决URLSearchParams is not undefined

//# console
npm i url-search-params-polyfill

//# mian.js
import 'url-search-params-polyfill';

以上内容于 2019-04-23 修改.

场景二 :如下图

有人说是在 iphone 5s 或者 6s 上会出现这种问题,肯定不是手机的 bug。于是我重现了场景,真的和设备无关

所以这个跟设备真的没有关系。知道问题所在,当然解决办法也有许多

方案一:暴力愚蠢型

//路由跳转前滚动条清零
document.body.scrollTop = document.documentElement.scrollTop = 0;
this.$router.push({ path: "/a/b/c" });

方案二:可行但不可选型

//给router 加一个监听,一旦改变,执行清零,然后再跳转
let routers = new Router({.....})
routers.beforeEach(function (to, from, next) {
    ......
    document.body.scrollTop = document.documentElement.scrollTop = 0
    next()
})

虽然可行,但是感觉着么干有点愚蠢。因为还有更好的写法,这个写法更佳优雅

方案三:最佳型

其实官方已经提供了当路由切换时,控制滚动位置的方式。scrollBehavior

使用方式如下:

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置 { x: number, y: number } |  { selector: string } |
  }
})

scrollBehavior 方法接收 tofrom 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。

所以假如要解决白屏的问题,可以着么干

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    return savedPosition || { x: 0, y: 0 }
  }
})

也就是说,当用户点击返回、前进 的时候,页面会滚动到之前 位置,(微信朋友圈的文章就是这样的,读一半返回,再进来接着刚才的位置阅读)

如果是新的 pagemounted 进来的时候,就重置为 0 。

完美的解决了这个问题。

但是这也是个问题,框架为什么不默认呢,假如自定义的时候可以 overwirte。

场景三: 缓存的原因(2019.4.15)

我们根据版本号(或者 hash)去控制缓存问题,当我们发布新版本,会发现 html 里面引用的版本号却是旧的版本号 ,这种情况是入口 index.html 文件被缓存了,很多时候我们设置禁止 html 文件被缓存,但依然会出现被缓存的情况。比如在头部加

<meta http-equiv="Expires" content="0" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-control" content="no-cache" />
<meta http-equiv="Cache" content="no-cache" />

仍然解决不了问题,关于 web 的缓存策略,推荐这篇文章: Http 缓存机制

一旦 index.html 被缓存了,之后我们使用了全量更新,也就是每次发版本之前会干掉之前的 js 和 css 文件,那么被缓存的 index.html 会无法加载之前旧的 js,css 还有一些其他的静态资源文件,而新的 js 和 css 则不会被加载,那么白屏就诞生了.

这个时候我们就要配合服务端来解决 index.html 的缓存问题
解决缓存的问题请转到这里:Vue index.html 入口缓存问题

[完]

我,秦始皇,打钱!