面临的挑战
- 服务端和客户端状态不一致:由于服务端和客户端渲染的时机不同,可能导致初始状态不一致,进而影响异步数据的加载和缓存。
- 缓存管理:如何有效地管理缓存,避免缓存过期或数据更新不及时,特别是在高并发情况下。
- 数据预取:在服务端渲染时,需要在合适的时机预取数据,同时要考虑预取数据的性能开销。
- 内存管理:在服务端,过多的缓存数据可能导致内存占用过高,需要合理的内存回收策略。
设计思路
- 统一状态管理:使用Vuex作为状态管理工具,确保服务端和客户端共享相同的状态结构。
- 数据预取:在服务端渲染时,通过路由钩子或生命周期钩子预取数据,并将数据填充到Vuex store中。
- 缓存策略:为每个异步请求设置缓存标识和过期时间,根据缓存标识判断数据是否已缓存,根据过期时间决定是否重新获取数据。
- 状态同步:在客户端激活时,将服务端渲染填充的Vuex状态同步到客户端,避免重复获取数据。
关键代码实现
1. 服务端数据预取
// 在服务端路由钩子中预取数据
import { createRouter } from './router'
import { createStore } from './store'
export default async (context) => {
const router = createRouter()
const store = createStore()
// 设置当前路由
router.push(context.url)
// 等待路由解析完成
await router.isReady()
// 获取匹配的路由组件
const matchedComponents = router.currentRoute.value.matched
// 预取数据
const prefetchPromises = matchedComponents
.filter(component => component.asyncData)
.map(component => component.asyncData({ store, route: router.currentRoute.value }))
// 等待所有预取数据的Promise完成
await Promise.all(prefetchPromises)
return { router, store }
}
2. 异步数据加载与缓存
// Vuex模块示例
const module = {
namespaced: true,
state: () => ({
data: null,
cacheTime: null,
CACHE_EXPIRE_TIME: 60 * 1000 // 缓存过期时间1分钟
}),
mutations: {
SET_DATA(state, data) {
state.data = data
state.cacheTime = new Date().getTime()
}
},
actions: {
async fetchData({ state, commit }) {
if (state.data && new Date().getTime() - state.cacheTime < state.CACHE_EXPIRE_TIME) {
return state.data
}
const response = await fetch('/api/data')
const data = await response.json()
commit('SET_DATA', data)
return data
}
}
}
3. 客户端状态同步
// 在客户端入口文件中同步状态
import { createApp } from 'vue'
import { createStore } from './store'
import App from './App.vue'
const store = createStore()
// 将服务端渲染时填充的状态同步到客户端
if (window.__INITIAL_STATE__) {
store.replaceState(window.__INITIAL_STATE__)
}
const app = createApp(App)
app.use(store)
app.mount('#app')
可能遇到的坑和解决方案
- 客户端激活时数据重复请求:解决方案是在客户端激活时,将服务端渲染填充的Vuex状态同步到客户端,如上述客户端状态同步代码所示。
- 缓存过期处理不当:需要严格按照设定的缓存过期时间来判断是否重新获取数据,并且在数据更新时及时更新缓存时间。
- 服务端内存占用过高:可以通过设置合理的缓存过期时间,定期清理缓存数据,或者采用LRU(最近最少使用)等缓存淘汰策略来管理内存。
- 跨域问题:在服务端预取数据时,可能会遇到跨域问题。可以通过在服务端设置代理或者使用CORS(跨域资源共享)来解决。