Vue SSR路由处理与一致性保障最佳实践之一
Vue SSR 中的路由处理与一致性保障
服务端路由处理
在服务端渲染(SSR)场景中,Vue路由的处理与纯客户端应用有显著不同。服务端需要预先确定要渲染的路由,并确保所有异步操作完成后再返回HTML。
router.onReady
等待异步路由
服务端需要等待路由解析完成才能开始渲染:
// 服务端入口文件
import { createApp } from './app'
export default context => {
return new Promise((resolve, reject) => {
const { app, router } = createApp()
// 设置服务端路由位置
router.push(context.url)
// 等待路由解析完成
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
// 无匹配路由时返回404
if (!matchedComponents.length) {
return reject({ code: 404 })
}
resolve(app)
}, reject)
})
}
实践建议:
- 始终使用
router.onReady
确保路由解析完成 - 处理404情况时返回适当的HTTP状态码
- 考虑路由重定向场景的特殊处理
路由数据预取(serverPrefetch
)
在服务端渲染时,我们通常需要预先获取组件所需数据:
// 组件定义
export default {
name: 'PostDetail',
async serverPrefetch() {
// 服务端预取数据
await this.fetchPostData(this.$route.params.id)
},
methods: {
fetchPostData(id) {
return store.dispatch('fetchPost', id)
}
}
}
数据预取流程:
实践建议:
- 使用
serverPrefetch
而非created
或mounted
进行数据获取 - 确保预取的数据能够被客户端访问(通过window.__INITIAL_STATE__等方式)
- 处理数据预取失败的情况,提供优雅降级方案
客户端与服务端路由一致性
SSR应用中最常见的挑战之一是保持客户端和服务端渲染结果的一致性,避免hydration不匹配错误。
避免hydration不匹配问题
hydration不匹配通常由以下原因引起:
- 服务端和客户端初始数据不一致
- 依赖于客户端特定API(如window对象)
- 时间敏感的操作(如Date.now())
- 随机数生成不一致
解决方案:
// 确保数据同步
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__)
}
// 客户端入口文件
const { app, router, store } = createApp()
router.onReady(() => {
// 添加路由钩子处理asyncData
router.beforeResolve((to, from, next) => {
const matched = router.getMatchedComponents(to)
const prevMatched = router.getMatchedComponents(from)
let diffed = false
const activated = matched.filter((c, i) => {
return diffed || (diffed = (prevMatched[i] !== c))
})
const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _)
if (!asyncDataHooks.length) {
return next()
}
Promise.all(asyncDataHooks.map(hook => hook({ store, route: to })))
.then(() => next())
.catch(next)
})
app.$mount('#app')
})
实践建议:
- 使用相同的状态管理初始化逻辑(如Vuex store)
- 避免在组件生命周期中直接调用可能产生差异的API
- 对于必须的客户端特定逻辑,使用
mounted
钩子而非created
- 开发环境下启用Vue的hydration警告,及时发现不匹配问题
路由数据同步策略
关键代码实现:
// 服务端数据注入
context.rendered = () => {
context.state = store.state
}
// 客户端模板
<!DOCTYPE html>
<html>
<head>
<!-- 初始状态注入 -->
<script>
window.__INITIAL_STATE__ = ${serialize(state)}
</script>
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>
总结
Vue SSR中的路由处理需要特别注意:
- 服务端路由解析:使用
router.onReady
确保所有异步路由解析完成 - 数据预取:通过
serverPrefetch
统一服务端和客户端数据获取方式 - 一致性保障:精心设计数据同步策略,避免hydration不匹配
- 错误处理:全面考虑各种边界情况(404、重定向、数据获取失败等)
遵循这些原则,可以构建出既保持SEO友好性,又具备流畅交互体验的Vue SSR应用。
评论已关闭