面试题答案
一键面试导航守卫在SSR环境中的特点
- 执行时机:在SSR场景下,导航守卫在服务端渲染过程中会在路由匹配阶段执行。与客户端渲染不同,它没有完整的DOM环境,主要用于数据获取、权限验证等与渲染相关的逻辑。
- 无浏览器相关操作:由于运行在服务端,不能直接操作浏览器对象,如
window
、document
等。这就限制了一些依赖浏览器环境的操作,如弹窗、本地存储等。
可能遇到的问题
- 上下文差异:服务端和客户端上下文不同,在服务端没有浏览器相关的全局对象。例如在守卫中不能直接使用
window.localStorage
来获取用户登录状态,需要从其他途径(如请求头中的认证信息)获取。 - 数据一致性:确保服务端预取的数据和客户端后续渲染的数据一致。如果服务端预取数据后,客户端又重新请求一遍不同的数据,可能导致页面闪烁或不一致的问题。
功能扩展与应用优化
- 数据预取:在服务端利用导航守卫进行数据预取,可以显著提高页面首次加载性能。例如,在进入一个商品详情页前,预取商品的详细信息。
- 优化思路:
- 缓存数据:在服务端可以使用缓存机制(如Redis)来避免重复获取相同的数据,减少数据库压力。
- 合并请求:如果多个组件需要的数据来自同一个数据源,可以合并这些请求,减少网络请求次数。
代码示例
假设使用Vue和Node.js搭建SSR项目,以下是在导航守卫中进行数据预取的示例:
- 安装依赖:
npm install vue vue - router express
- 服务端代码(server.js):
const express = require('express')
const { createSSRApp } = require('vue')
const { createRouter } = require('./router')
const app = express()
app.get('*', async (req, res) => {
const ssrApp = createSSRApp()
const router = createRouter()
router.push(req.url)
await router.isReady()
const matchedComponents = router.currentRoute.value.matched.map(record => record.components.default)
const prefetchPromises = []
matchedComponents.forEach(component => {
if (component.asyncData) {
prefetchPromises.push(component.asyncData({ route: router.currentRoute.value }))
}
})
await Promise.all(prefetchPromises)
const html = renderToString(ssrApp)
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<div id="app">${html}</div>
</body>
</html>
`)
})
const port = 3000
app.listen(port, () => {
console.log(`Server is running on port ${port}`)
})
- 路由配置(router.js):
import { createRouter, createWebHistory } from 'vue - router'
import Home from './views/Home.vue'
import Product from './views/Product.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/product/:id',
name: 'Product',
component: Product,
beforeEnter: async (to, from) => {
// 模拟数据预取
const productId = to.params.id
const product = await fetchProduct(productId)
to.meta.product = product
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export function createRouter() {
return router
}
- 商品详情页组件(Product.vue):
<template>
<div>
<h1>{{ $route.meta.product.title }}</h1>
<p>{{ $route.meta.product.description }}</p>
</div>
</template>
<script setup>
// 模拟从API获取商品数据
async function fetchProduct(id) {
return {
title: 'Sample Product',
description: 'This is a sample product description'
}
}
</script>
通过上述代码,在服务端渲染时,beforeEnter
导航守卫会在进入商品详情页前预取商品数据,将数据挂载到to.meta
上,从而在组件渲染时可以直接使用,提高了首次加载性能,并保证了服务端和客户端数据的一致性。