【偏门药方】请不要在Vue初始化时获取Route内容
高性能萝卜子镇楼!(拖了很久的记录,在新公司有点身心俱疲,调节了许久...
病症澄清
笔者接单跟踪一个线上极低概率出现的BUG,反馈详情:APP内嵌的H5-A页面,在里面跳转另一个项目H5-B页面,然后又从H5-B页面跳转回H5-A页面,回到项目A的时候却白屏了,无法稳定复现,没有任何规律。
无论是本地还是测试环境APP都无法复现,查了一整天仍无头绪,实在是汗流浃背了。最后通过一个埋点的缺失锁定了问题代码片段,这个片段的逻辑是在vue app挂载时获取vue-router中route的meta然后执行某些动作。实际上在这个时间节点的meta仍然是个空对象,导致if分支内的逻辑无法继续执行。但是,为什么这是个偶现的问题呢?
// App.vue
export default {
// ...
mounted() {
// ...
await doSomething()
if (this.$route.meta.redirect) {
// 重定向到目标页面...
}
},
// ...
}
Vue、Vue-router的初始化孰先孰后?
其实这里并没有反直觉,Vue完成初始化后Vue-router基本也完成了初始化,但为什么会出现笔者上面提及的BUG?
/** main.js */
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
console.log('before use router')
app.use(router)
console.log('after use router')
app.mount('#app')
/** router/index.js */
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
meta: {
a: 2
}
},
{
path: '/about',
name: 'about',
meta: {
a: 2111
},
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
}
]
})
router.beforeEach(async (to, from) => {
console.log('router.beforeEach')
console.log('router.beforeEach from',from)
console.log('router.beforeEach to',to)
})
export default router
/** app.vue */
<script setup>
import { onMounted } from 'vue'
import { RouterLink, RouterView, useRoute, useRouter } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import { client } from '@gradio/client'
const route = useRoute();
const router = useRouter();
onMounted(() => {
console.log('app begin mounted')
console.log('app begin mounted meta', route.meta);
setTimeout(() => {
console.log('settimeout meta', route.meta);
}, 500)
router.isReady().then(() => {
console.log('router isReady meta', route.meta);
})
console.log('app mounted')
})
</script>
上面代码运行后控制台打印出了下列数据:
可以看到,app.vue的mounted阶段都还无法正常的输出路由中meta的内容,而是输出默认Route实例的meta空对象。所以就导致了上面提及的白屏bug,那么为什么是个非必现的情况呢?
因为在项目中的mounted回调有一个阻塞型的异步任务,这个异步任务的完成耗时直接影响后续meta的获取,这个任务耗时越长router初始化的完成几率就越大,也就越不可能出现白屏。
问题的根源基本确定,那么就该聊聊怎么避免这个情况了。
药方开出
尽量不要在Vue初始化时获取Route内容
很简单,能不在初始化获取Route内容就尽量不要获取,如果一定需要在初始化的时候获取则可以使用router的isReady
方法,确保在router已经完成初始化后再进行我们的业务操作,这样就不会出现无法正常获取Route内容的情况了。
碎碎念: 这次遇到的问题解决起来其实很简单很简单,就是整个定位的过程比较麻烦,又碰上偶现的情况,没有详细的埋点上报还真不太好定位到。做个记录,希望下次再遇到就不会汗流浃背了。
转载自:https://juejin.cn/post/7397013935105294387