Vue3 SSR
什么是 SSR
服务端渲染(Server-Side Rendering)在服务器端将 Vue 组件渲染为 HTML。
优势
- 更好的 SEO
- 更快的首屏加载
劣势
- 开发限制
- 服务器负载
- 部署复杂
使用 Vite SSR
项目结构
src/
├── components/
├── pages/
├── entry-client.js
├── entry-server.js
└── main.js
entry-client.js
import { createApp } from './main'
const { app, router } = createApp()
router.isReady().then(() => {
app.mount('#app')
})
entry-server.js
import { createApp } from './main'
import { renderToString } from 'vue/server-renderer'
export async function render(url) {
const { app, router } = createApp()
await router.push(url)
await router.isReady()
const html = await renderToString(app)
return html
}
main.js
import { createSSRApp } from 'vue'
import { createRouter, createMemoryHistory, createWebHistory } from 'vue-router'
import App from './App.vue'
export function createApp() {
const app = createSSRApp(App)
const router = createRouter({
history: import.meta.env.SSR
? createMemoryHistory()
: createWebHistory(),
routes: [...]
})
app.use(router)
return { app, router }
}
server.js
import express from 'express'
import { render } from './dist/server/entry-server.js'
const app = express()
app.get('*', async (req, res) => {
const html = await render(req.url)
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>SSR App</title>
</head>
<body>
<div id="app">${html}</div>
<script type="module" src="/src/entry-client.js"></script>
</body>
</html>
`)
})
app.listen(3000)
使用 Nuxt 3
创建项目
npx nuxi@latest init my-app
cd my-app
yarn install
yarn dev
目录结构
my-app/
├── assets/
├── components/
├── composables/
├── layouts/
├── middleware/
├── pages/
├── plugins/
├── public/
├── server/
├── app.vue
└── nuxt.config.ts
页面
<!-- pages/index.vue -->
<template>
<div>
<h1>{{ title }}</h1>
<NuxtLink to="/about">About</NuxtLink>
</div>
</template>
<script setup>
const { data: title } = await useFetch('/api/title')
</script>
布局
<!-- layouts/default.vue -->
<template>
<div>
<header>
<nav>...</nav>
</header>
<slot />
<footer>...</footer>
</div>
</template>
API 路由
// server/api/hello.ts
export default defineEventHandler((event) => {
return {
message: 'Hello World'
}
})
数据获取
<script setup>
// useFetch
const { data, pending, error, refresh } = await useFetch('/api/data')
// useAsyncData
const { data } = await useAsyncData('key', () => $fetch('/api/data'))
// useLazyFetch
const { pending, data } = useLazyFetch('/api/data')
</script>
状态管理
// composables/useCounter.ts
export const useCounter = () => {
const count = useState('counter', () => 0)
const increment = () => count.value++
const decrement = () => count.value--
return {
count,
increment,
decrement
}
}
中间件
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const user = useState('user')
if (!user.value) {
return navigateTo('/login')
}
})
<script setup>
definePageMeta({
middleware: 'auth'
})
</script>
插件
// plugins/myPlugin.ts
export default defineNuxtPlugin((nuxtApp) => {
return {
provide: {
hello: (msg: string) => `Hello ${msg}!`
}
}
})
<script setup>
const { $hello } = useNuxtApp()
console.log($hello('World'))
</script>
部署
静态生成
yarn generate
Node.js 服务器
yarn build
node .output/server/index.mjs