Vue中mounted早于异步created执行完成触发,如何延迟挂载?
问题原因说明
Vue 的 async 生命周期钩子不会阻塞后续生命周期执行:async created 只是让钩子函数本身返回 Promise,Vue 内部不会等待这个 Promise 执行完成再触发 mounted,所以你会看到日志输出顺序和预期不符,数据返回前挂载的空模板会覆盖 SSR 渲染的内容,导致页面空白。
可行解决方案
方案1:延迟挂载Vue实例(兼容性最好,适合Vue2/Vue3)
先完成数据请求,再初始化Vue实例挂载到DOM,从根源避免空模板覆盖SSR内容的问题,不需要通过props传递数据:
// 原挂载逻辑 /* import CakeShopList from './CakeShopList.vue' new Vue({ render: h => h(CakeShopList) }).$mount('#cake-shop-list') */ // 修改后逻辑 import CakeShopList from './CakeShopList.vue' // 提前发起数据请求 const fetchCakeShops = (params) => { // 你的接口请求逻辑 } fetchCakeShops({page: 1}).then(cakeShops => { // 数据拿到后再实例化Vue并挂载 new Vue({ data() { return { // 把预加载的数据存在根实例,子组件可以直接从根实例取,也可以提前存入Vuex/Pinia preloadCakeShops: cakeShops } }, render: h => h(CakeShopList) }).$mount('#cake-shop-list') })
子组件内直接读取预加载的数据即可:
// CakeShopList.vue export default { data() { return { cakeShops: [] } }, created() { // 直接取根实例预加载的数据,不需要等待请求 this.cakeShops = this.$root.preloadCakeShops // 也可以从Vuex/Pinia中取预存的数据 } }
方案2:Vue3使用Suspense内置组件
如果是Vue3项目,可以用Suspense包裹异步组件,自动等待异步逻辑执行完成后再渲染组件:
父组件代码
<template> <Suspense> <template #default> <CakeShopList /> </template> <!-- fallback插槽放置SSR渲染的静态内容,加载过程中展示,不会出现空白 --> <template #fallback> <ul> <li>shopA</li> <li>shopB</li> </ul> </template> </Suspense> </template>
子组件代码(使用async setup)
<script setup> const fetchCakeShops = (params) => { // 接口请求逻辑 } // Suspense会等待这个Promise执行完成再渲染default插槽内容 const cakeShops = await fetchCakeShops({page:1}) </script> <template> <div> <ul> <li v-for="shop in cakeShops" :key="shop.id" @click="onClickShop(shop.id)">{{shop.name}}</li> </ul> </div> </template>
方案3:组件内加载状态切换
如果不想修改挂载逻辑,可以在组件内双模板切换,加载过程中保留静态SSR内容,避免空白:
<template> <div> <!-- 数据加载完成前展示SSR静态内容 --> <ul v-if="!isLoaded"> <li>shopA</li> <li>shopB</li> </ul> <!-- 数据加载完成后展示动态绑定列表 --> <ul v-else> <li v-for="shop in cakeShops" :key="shop.id" @click="onClickShop(shop.id)">{{shop.name}}</li> </ul> </div> </template> <script> export default { data() { return { cakeShops: [], isLoaded: false } }, async created() { this.cakeShops = await this.fetchCakeShops({page: 1}) this.isLoaded = true } } </script>
内容的提问来源于stack exchange,提问作者yasuda_t
相关产品推荐
相关产品推荐

