Server-Side Rendering (SSR)
Advanced Use of asyncData
Advanced asyncData Pattern:
// pages/products/_id.vue
export default defineComponent({
async asyncData({ params, $axios, error, req }) {
try {
// 1. Parallel requests to multiple data sources
const [product, related] = await Promise.all([
$axios.$get(`/api/products/${params.id}`),
$axios.$get(`/api/products/${params.id}/related`)
])
// 2. Use request context
const userAgent = req?.headers['user-agent'] || ''
const isMobile = /mobile/i.test(userAgent)
// 3. Data preprocessing
const enrichedProduct = {
...product,
isFavorite: checkUserFavorite(product.id), // Client state check
meta: generateMetaTags(product, isMobile)
}
return {
product: enrichedProduct,
relatedProducts: related
}
} catch (err) {
// 4. Error handling
error({
statusCode: err.response?.status || 500,
message: err.message
})
}
}
})SSR Data Prefetching Strategy:
// composables/useProductData.ts
export const useProductData = (id: string) => {
// 1. Server-first fetching
const { data, error } = await useAsyncData(
`product-${id}`,
() => $fetch(`/api/products/${id}`),
{
server: true, // Force server-side fetching
lazy: false // Immediate execution
}
)
// 2. Client-side supplemental data
const clientSideData = ref(null)
if (process.client) {
onMounted(async () => {
clientSideData.value = await $fetch(`/api/products/${id}/client-data`)
})
}
return {
product: computed(() => data.value || clientSideData.value),
error
}
}SSR Performance Optimization and Cache Strategies
Multi-Level Cache Implementation:
// server/middleware/cache.ts
export default defineEventHandler(async (event) => {
const cache = useCache()
const key = getRequestCacheKey(event)
// 1. Check memory cache
if (cache.has(key)) {
return cache.get(key)
}
// 2. Check Redis cache
const redisData = await redis.get(key)
if (redisData) {
const data = JSON.parse(redisData)
cache.set(key, data) // Backfill memory cache
return data
}
// 3. Execute actual request
const response = await handleRequest(event)
// 4. Set cache
const ttl = getCacheTTL(event) // Dynamically calculate TTL
cache.set(key, response, ttl)
await redis.set(key, JSON.stringify(response), 'EX', ttl)
return response
})Component-Level Caching:
<!-- components/ProductList.vue -->
<script setup>
// Use <nuxt:cache> directive for component caching
definePageMeta({
cache: {
key: ({ params }) => `products-${params.category}`,
maxAge: 60 * 5 // 5-minute cache
}
})
</script>
<template>
<nuxt:cache>
<div v-for="product in products" :key="product.id">
<!-- Product list content -->
</div>
</nuxt:cache>
</template>SSR Security and Access Control
Role-Based Access Control:
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const user = useUserStore()
const requiredRoles = to.meta.requiredRoles || []
if (requiredRoles.length > 0 && !requiredRoles.some(role => user.roles.includes(role))) {
return navigateTo('/unauthorized')
}
// Sensitive data filtering
if (to.meta.sensitiveData) {
to.meta.dataFilter = (data) => {
return filterSensitiveFields(data, user.permissions)
}
}
})Request Validation Middleware:
// server/middleware/validation.ts
export default defineEventHandler(async (event) => {
// 1. CSRF validation
if (isUnsafeMethod(event.req.method)) {
verifyCSRFToken(event)
}
// 2. Rate limiting
const ip = event.node.req.headers['x-forwarded-for'] || event.node.req.socket.remoteAddress
await rateLimit(ip)
// 3. Request body validation
if (event.req.method === 'POST') {
const schema = getValidationSchema(event.node.req.url)
if (schema) {
await schema.validate(await readBody(event))
}
}
})SSR Use Cases
Dynamic Content Rendering:
<!-- pages/news/_id.vue -->
<script setup>
const { data } = await useAsyncData('news-item', () => {
return $fetch(`/api/news/${route.params.id}`, {
headers: {
'X-Request-Freshness': 'always' // Force fetch latest data
}
})
})
</script>
<template>
<article v-if="data">
<h1>{{ data.title }}</h1>
<div v-html="data.content"></div>
<!-- Dynamic ad slot -->
<AdSlot :position="'news-'+data.id" />
</article>
</template>User Personalized Experience:
// composables/usePersonalization.ts
export const usePersonalization = () => {
const user = useUserStore()
// Server-side personalized data injection
const personalizedData = ref(null)
if (process.server) {
personalizedData.value = {
recommendations: getRecommendations(user.id),
theme: getUserThemePreference(user.id)
}
}
// Client-side supplemental personalization
if (process.client) {
onMounted(async () => {
if (!personalizedData.value) {
personalizedData.value = await fetchPersonalization()
}
})
}
return { personalizedData }
}Combining SSR and ISR
Hybrid Rendering Strategy:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
isr: {
// Default ISR configuration
revalidate: 60
}
},
routeRules: {
// Specific route overrides global config
'/products/:id': {
ssr: true, // Enable SSR
isr: { // Override ISR settings
revalidate: 30, // 30-second revalidation
swr: 300 // 5-minute stale-while-revalidate
}
},
'/blog': {
ssr: false, // Disable SSR
isr: { // Pure static generation + ISR
revalidate: 3600 // 1-hour revalidation
}
}
}
})Dynamic Route Hybrid Mode:
// server/routes.js
export default defineRoutes({
// Dynamic route SSR handling
'/user/:id': {
handler: async (req, res) => {
const user = await getUserFromDB(req.params.id)
if (!user) throw createError(404)
// Critical user data SSR rendering
const html = await renderSSR('user-profile', { user })
// Non-critical data fetched client-side
const script = `
<script>
window.__USER_DATA__ = ${JSON.stringify(user)}
fetch('/api/user/${req.params.id}/preferences')
.then(res => res.json())
.then(data => {
window.__USER_PREFS__ = data
})
</script>
`
res.send(html + script)
},
// ISR fallback
isr: {
revalidate: 60
}
}
})Static Site Generation (SSG)
Advanced Use of asyncData and fetch
Prerendering Data Strategy:
// pages/docs/_slug.vue
export default defineComponent({
async asyncData({ params, $content }) {
// 1. Prefetch content during static generation
const doc = await $content('docs', params.slug).fetch()
// 2. Generate additional metadata
const toc = generateTableOfContents(doc.content)
const related = await $content('docs')
.where({ tags: { $contains: doc.tags[0] } })
.limit(3)
.fetch()
return {
document: {
...doc,
toc,
related
}
}
},
// Client-side data supplementation
data() {
return {
comments: [] // Client fetches comments
}
},
mounted() {
this.loadComments()
},
methods: {
async loadComments() {
this.comments = await this.$axios.$get(`/api/comments/${this.$route.params.slug}`)
}
}
})Incremental Static Regeneration (ISR) Integration:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
isr: {
// Default configuration
revalidate: 3600 // 1 hour
}
},
// Dynamic route ISR configuration
generate: {
routes: async () => {
// 1. Get static routes
const staticRoutes = await getStaticRoutes()
// 2. Add dynamic route placeholders
const dynamicRoutes = await getDynamicRouteSlugs()
return [...staticRoutes, ...dynamicRoutes.map(slug => `/blog/${slug}`)]
}
}
})SSG Performance Optimization and Cache Strategies
Static Asset Optimization:
// nuxt.config.ts
export default defineNuxtConfig({
app: {
// Static asset CDN configuration
cdnURL: process.env.CDN_URL,
// Preload critical resources
head: {
link: [
{ rel: 'preload', href: '/_nuxt/runtime.js', as: 'script' },
{ rel: 'preload', href: '/_nuxt/commons.app.js', as: 'script' }
]
}
},
// Build optimization
build: {
// Extract CSS to separate file
extractCSS: true,
// Optimize static assets
optimization: {
splitChunks: {
chunks: 'all',
automaticNameDelimiter: '.'
}
}
}
})Cache Control Strategy:
# Static asset response header example
Cache-Control: public, max-age=31536000, immutable
Content-Disposition: inline
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2022 07:28:00 GMTSSG Security and Access Control
Static Site Security Strategy:
// server/middleware/security.ts
export default defineEventHandler(async (event) => {
// 1. Content Security Policy
setHeader(event, 'Content-Security-Policy', [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' cdn.example.com",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: cdn.example.com"
].join('; '))
// 2. Subresource Integrity (SRI)
if (event.node.req.url.includes('/_nuxt/')) {
const integrity = generateSRIHash(event.node.req.url)
setHeader(event, 'Integrity', integrity)
}
// 3. XSS Protection
setHeader(event, 'X-XSS-Protection', '1; mode=block')
setHeader(event, 'X-Content-Type-Options', 'nosniff')
})Sensitive Content Handling:
<!-- pages/protected.vue -->
<script setup>
const { data } = await useAsyncData('protected-content', () => {
return $fetch('/api/protected', {
headers: {
'Authorization': `Bearer ${useCookie('token').value}`
}
})
})
// Client-side conditional rendering
const showContent = computed(() => {
return process.client && hasAccess(data.value)
})
</script>
<template>
<div v-if="showContent">
<!-- Sensitive content -->
</div>
<div v-else>
<NuxtLink to="/login">Please log in to view content</NuxtLink>
</div>
</template>SSG Use Cases
Documentation Website Generation:
// nuxt.config.ts
export default defineNuxtConfig({
// Document route generation
generate: {
routes: async () => {
const { $content } = require('@nuxt/content')
const files = await $content({ deep: true }).only(['path']).fetch()
return files.map(file => file.path)
}
},
// Document-specific configuration
content: {
documentDriven: true,
navigation: {
fields: ['title', 'description', 'slug']
}
}
})Blog System Implementation:
// server/api/posts.ts
export default defineEventHandler(async (event) => {
// 1. Pre-generate all posts during static generation
if (process.static) {
const posts = await getPostsFromDB()
await generateStaticPosts(posts)
return { status: 'generated' }
}
// 2. Dynamic request handling
const { slug } = getQuery(event)
if (slug) {
return getPostBySlug(slug)
}
return getAllPosts()
})Combining SSG and ISR
Hybrid Rendering Strategy:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
isr: {
// Global ISR configuration
revalidate: 3600 // 1 hour
}
},
routeRules: {
// Static page + ISR fallback
'/blog/:slug': {
ssr: false, // Disable SSR
isr: {
revalidate: 300, // 5-minute revalidation
swr: 3600 // 1-hour stale-while-revalidate
}
},
// Critical page SSR + ISR
'/products/:id': {
ssr: true, // Enable SSR
isr: {
revalidate: 60, // 1-minute revalidation
swr: 600 // 10-minute stale-while-revalidate
}
}
}
})Dynamic Content Staticization:
// server/routes/blog.js
export default defineRoutes({
'/blog/:slug': {
handler: async (req, res) => {
// 1. Check if static file exists
const staticFile = path.join(__dirname, '../dist/blog', req.params.slug, 'index.html')
if (fs.existsSync(staticFile)) {
return res.sendFile(staticFile)
}
// 2. Dynamically generate and cache
const post = await getPostFromDB(req.params.slug)
if (!post) throw createError(404)
const html = await renderSSR('blog-post', { post })
await cacheStaticPage(`/blog/${req.params.slug}`, html)
res.send(html)
},
isr: {
revalidate: 300 // 5-minute revalidation
}
}
})Real-Time Application Development
Combining WebSocket with Nuxt.js
WebSocket Integration:
// plugins/websocket.ts
export default defineNuxtPlugin((nuxtApp) => {
if (process.client) {
const socket = new WebSocket(process.env.WS_URL)
// Connection management
const connection = {
socket,
connected: false,
reconnectAttempts: 0,
connect() {
socket.onopen = () => {
this.connected = true
this.reconnectAttempts = 0
}
socket.onmessage = (event) => {
const data = JSON.parse(event.data)
nuxtApp.hook('websocket:message', data)
}
socket.onclose = () => {
this.connected = false
this.reconnect()
}
},
reconnect() {
if (this.reconnectAttempts < 5) {
setTimeout(() => {
this.reconnectAttempts++
this.connect()
}, 1000 * Math.pow(2, this.reconnectAttempts))
}
},
send(data) {
if (this.connected) {
socket.send(JSON.stringify(data))
} else {
// Add to message queue
this.messageQueue.push(data)
}
}
}
connection.connect()
// Provide global access
return {
provide: {
websocket: connection
}
}
}
})Real-Time Data Updates:
<!-- components/RealTimeFeed.vue -->
<script setup>
const feedItems = ref([])
const { $websocket } = useNuxtApp()
// Listen for WebSocket messages
onMounted(() => {
$websocket.socket.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.type === 'FEED_UPDATE') {
feedItems.value = [...feedItems.value, data.item]
}
}
})
// Manually send message
const sendMessage = (message) => {
$websocket.send({
type: 'NEW_MESSAGE',
content: message
})
}
</script>
<template>
<div>
<div v-for="item in feedItems" :key="item.id">
{{ item.content }}
</div>
<input v-model="newMessage" @keyup.enter="sendMessage(newMessage)" />
</div>
</template>Combining GraphQL with Nuxt.js
Apollo Client Integration:
// plugins/apollo.ts
export default defineNuxtPlugin((nuxtApp) => {
const apolloClient = new ApolloClient({
link: createHttpLink({
uri: process.env.GRAPHQL_ENDPOINT,
headers: {
Authorization: useCookie('token')?.value || ''
}
}),
cache: new InMemoryCache(),
ssrMode: process.server
})
return {
provide: {
apollo: apolloClient
}
}
})SSR Data Fetching:
<!-- pages/products.vue -->
<script setup>
const { result, loading, error } = useQuery(gql`
query GetProducts {
products {
id
name
price
}
}
`, null, {
// SSR options
server: true,
prefetch: true
})
// Client-side cache strategy
const { data } = useApolloCache({
query: gql`...`,
update: (cache, { data }) => {
cache.modify({
fields: {
products(existing = []) {
return [...existing, ...data.newProducts]
}
}
})
}
})
</script>Real-Time Collaborative Editing Implementation
CRDT Algorithm Integration:
// composables/useCollaborativeEditing.ts
export const useCollaborativeEditing = (docId: string) => {
const localState = ref({})
const remoteStates = ref(new Map<string, any>())
// 1. Initialize CRDT document
const crdt = new Y.Doc()
const provider = new WebsocketProvider(
process.env.WS_URL,
'document-' + docId,
crdt,
{ connect: false }
)
// 2. Connection management
onMounted(() => {
provider.connect()
// Listen for remote changes
provider.awareness.on('change', () => {
const clients = provider.awareness.getStates()
remoteStates.value = new Map(
Object.entries(clients).map(([id, state]) => [id, state])
)
})
// Apply remote changes
crdt.on('update', (update) => {
Y.applyUpdate(localState.value, update)
})
})
// 3. Local edit handling
const applyLocalChange = (change) => {
Y.applyUpdate(crdt, change)
}
// 4. State synchronization
const getState = () => {
return {
local: localState.value,
remote: Object.fromEntries(remoteStates.value)
}
}
return {
state: getState(),
applyChange: applyLocalChange
}
}OT Algorithm Implementation Example:
// server/ot-server.ts
export class OTServer {
private documents: Map<string, TextOperation[]> = new Map()
// Apply client operation
applyOperation(docId: string, clientId: string, operation: TextOperation) {
// 1. Get current document state
let docOps = this.documents.get(docId) || []
// 2. Transform operation to resolve conflicts
const transformedOp = docOps.reduce((op, serverOp) => {
return transformOperation(op, serverOp)
}, operation)
// 3. Update document state
docOps.push(transformedOp)
this.documents.set(docId, docOps)
// 4. Broadcast to other clients
this.broadcast(docId, clientId, transformedOp)
}
// Operation transformation algorithm
private transformOperation(op1: TextOperation, op2: TextOperation): TextOperation {
// Implement OT transformation logic
// ...
}
}Performance Optimization for Real-Time Applications
WebSocket Connection Optimization:
// plugins/websocket-optimized.ts
export default defineNuxtPlugin((nuxtApp) => {
// 1. Connection pool management
const connectionPool = new Map<string, WebSocket>()
// 2. Message batching
const messageQueue = new Map<string, any[]>()
let batchTimer: number
const flushBatch = (channel: string) => {
if (messageQueue.has(channel)) {
const messages = messageQueue.get(channel)
const ws = connectionPool.get(channel)
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(messages))
}
messageQueue.delete(channel)
}
}
// 3. Heartbeat detection
const startHeartbeat = (ws: WebSocket) => {
const interval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'HEARTBEAT' }))
} else {
clearInterval(interval)
}
}, 30000)
return () => clearInterval(interval)
}
return {
provide: {
optimizedWebSocket: {
send(channel: string, message: any) {
// Batch messages
if (!messageQueue.has(channel)) {
messageQueue.set(channel, [])
batchTimer = setTimeout(() => flushBatch(channel), 50)
}
messageQueue.get(channel).push(message)
},
connect(channel: string) {
// Connection reuse
if (!connectionPool.has(channel)) {
const ws = new WebSocket(`${process.env.WS_URL}/${channel}`)
connectionPool.set(channel, ws)
// Start heartbeat
const stopHeartbeat = startHeartbeat(ws)
ws.onclose = () => {
connectionPool.delete(channel)
stopHeartbeat()
}
}
}
}
}
}
})Data Compression and Binary Transmission:
// server/websocket-compression.ts
export class CompressedWebSocket {
private ws: WebSocket
private compressor = new zlib.BrotliCompress()
private decompressor = new zlib.BrotliDecompress()
constructor(ws: WebSocket) {
this.ws = ws
this.setupHandlers()
}
private setupHandlers() {
this.ws.on('message', (data) => {
// Decompress message
this.decompressor.write(data, (err, decompressed) => {
if (!err) {
const message = JSON.parse(decompressed.toString())
this.handleMessage(message)
}
})
})
// Send compressed message
this.send = (message) => {
const json = JSON.stringify(message)
this.compressor.write(json, (err, compressed) => {
if (!err) {
this.ws.send(compressed)
}
})
}
}
}Security and Access Control for Real-Time Applications
WebSocket Authentication:
// server/middleware/ws-auth.ts
export default defineEventHandler(async (event) => {
if (event.node.req.headers['upgrade']?.toLowerCase() !== 'websocket') {
return // Skip non-WebSocket requests
}
// 1. Get token from query parameters
const token = getQuery(event).token as string
// 2. Verify JWT
try {
const decoded = verifyJWT(token)
event.context.user = decoded
// 3. Bind user to WebSocket
event.node.req.websocketUser = decoded
} catch (err) {
throw createError({
statusCode: 401,
message: 'WebSocket authentication failed'
})
}
})Real-Time Data Permission Filtering:
// server/ws-handlers.ts
export const handleRealtimeUpdate = (user: User, data: any) => {
// 1. Data permission check
if (data.type === 'DOCUMENT_UPDATE') {
if (!hasDocumentAccess(user.id, data.documentId)) {
throw new Error('Unauthorized document access')
}
}
// 2. Field-level permission filtering
const filteredData = filterSensitiveFields(data, user.permissions)
// 3. Broadcast to authorized users
broadcastToAuthorizedUsers(
data.targetUsers || [user.id],
filteredData
)
}
// Client-side permission verification
export const useRealtimePermission = (requiredPermission: string) => {
const { $websocket } = useNuxtApp()
const hasPermission = ref(false)
onMounted(async () => {
const userPermissions = await $websocket.fetchUserPermissions()
hasPermission.value = userPermissions.includes(requiredPermission)
})
return { hasPermission }
}



