服务端渲染(SSR)集成方案
服务端渲染(SSR)集成方案
Vite.js 作为现代前端构建工具,其原生支持服务端渲染(SSR)的能力让开发者能够轻松构建同构应用。SSR 在首屏性能、SEO 优化等方面具有显著优势,Vite 通过独特的架构设计解决了传统 SSR 方案中的开发体验问题。
Vite SSR 核心原理
Vite 的 SSR 实现基于其原生 ESM 特性,主要包含两个关键部分:
- 服务端构建:将应用代码转换为 Node.js 可执行的 CommonJS 格式
- 客户端构建:生成浏览器可运行的静态资源
// vite.config.js
export default defineConfig({
build: {
ssr: true, // 启用 SSR 构建模式
ssrManifest: true, // 生成 SSR 清单文件
rollupOptions: {
input: '/src/entry-server.js' // SSR 入口文件
}
}
})
基础集成方案
服务端入口配置
创建 SSR 专用的入口文件,通常命名为 entry-server.js
:
import { createSSRApp } from 'vue'
import App from './App.vue'
export function createApp() {
const app = createSSRApp(App)
return { app }
}
客户端激活
客户端需要特殊的激活逻辑来接管 SSR 生成的内容:
// entry-client.js
import { createSSRApp } from 'vue'
import App from './App.vue'
const app = createSSRApp(App)
app.mount('#app')
路由与数据预取
SSR 应用中路由和数据预取是关键环节:
// router.js
import { createMemoryHistory, createRouter } from 'vue-router'
export function createRouter() {
return createRouter({
history: import.meta.env.SSR
? createMemoryHistory()
: createWebHistory(),
routes: [...]
})
}
// 组件内数据预取
export async function asyncData({ route }) {
const res = await fetch(`/api/data/${route.params.id}`)
return res.json()
}
状态管理集成
在 SSR 中需要特别注意状态同步:
// store.js
import { createPinia } from 'pinia'
export function createStore() {
const pinia = createPinia()
return pinia
}
// 服务端渲染时初始化状态
if (import.meta.env.SSR) {
const store = useStore()
await store.fetchInitialData()
}
构建与部署配置
Vite SSR 需要特殊的构建配置:
// package.json
{
"scripts": {
"build": "vite build && vite build --ssr",
"serve": "node server.js"
}
}
// server.js
import express from 'express'
import { createServer } from 'vite'
import { render } from './dist/server/entry-server.js'
const app = express()
app.use('*', async (req, res) => {
const html = await render(req.url)
res.send(html)
})
app.listen(3000)
性能优化技巧
- 组件级缓存:对静态组件实施缓存策略
import { ssrCacheKey } from 'vite-plugin-ssr-cache'
export default {
name: 'StaticComponent',
serverCacheKey: () => ssrCacheKey('static-component')
}
- 流式渲染:使用 Node.js 流提高 TTFB
import { renderToNodeStream } from 'vue/server-renderer'
app.use('*', async (req, res) => {
const stream = renderToNodeStream(app)
stream.pipe(res, { end: false })
stream.on('end', () => res.end())
})
常见问题解决方案
跨请求状态污染
// 使用工厂函数创建应用实例
export function createApp() {
const app = createSSRApp(App)
const store = createStore()
app.use(store)
return { app, store }
}
客户端激活失败
确保客户端和服务器生成的 HTML 结构完全一致:
<div id="app"><!--ssr-outlet--></div>
环境变量处理
// vite.config.js
define: {
'import.meta.env.SSR': JSON.stringify(false)
}
高级集成模式
微前端 SSR 集成
// 主应用配置
export default defineConfig({
build: {
ssr: true,
rollupOptions: {
external: ['remote-app/entry-server']
}
}
})
// 服务端集成
import remoteApp from 'remote-app/entry-server'
const remoteHtml = await remoteApp.render(req.url)
边缘渲染方案
// Cloudflare Workers 示例
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const { render } = await import('./dist/server/entry-server.js')
return new Response(await render(request.url), {
headers: { 'Content-Type': 'text/html' }
})
}
调试与错误处理
开发阶段启用 SSR 调试:
// vite.config.js
export default defineConfig({
server: {
middlewareMode: 'ssr',
watch: {
usePolling: true,
interval: 100
}
}
})
错误边界处理:
// 错误边界组件
export default {
async setup() {
try {
await asyncData()
} catch (err) {
if (import.meta.env.SSR) {
throw err // 服务端抛出错误
}
// 客户端处理错误
}
}
}
测试策略
SSR 应用需要特殊的测试方案:
// 渲染测试示例
import { renderToString } from 'vue/server-renderer'
import App from './App.vue'
test('SSR renders correctly', async () => {
const html = await renderToString(App)
expect(html).toContain('Expected Content')
})
// 客户端激活测试
test('Client hydration', async () => {
const container = document.createElement('div')
container.innerHTML = serverHtml
await render(App, { container })
expect(container.textContent).toBe('Expected Content')
})
生态工具整合
与 Nuxt.js 对比
// Nuxt 自动处理 SSR 配置
export default defineNuxtConfig({
ssr: true,
vite: {
// 自定义 Vite 配置
}
})
使用 vite-plugin-ssr
// vite.config.js
import ssr from 'vite-plugin-ssr'
export default defineConfig({
plugins: [ssr()]
})
// 页面文件
export { Page }
export function onBeforeRender() {
return { pageContext: { data: fetchData() } }
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn