阿里云主机折上折
  • 微信号
Current Site:Index > Special handling for server-side rendering

Special handling for server-side rendering

Author:Chuan Chen 阅读数:23829人阅读 分类: Vue.js

Special Handling for Server-Side Rendering

Vue 3's Server-Side Rendering (SSR) differs significantly from client-side rendering, with the core challenge lying in how to handle component lifecycles, state management, and DOM operations across different environments. The runtime must distinguish the current execution context and replace or downgrade specific APIs accordingly.

Environment Variables and Global State Isolation

The primary issue in SSR is avoiding singleton state pollution. In a Node.js environment, each request should receive a fresh application instance:

// Use a factory function to avoid singletons  
export const createApp = () => {  
  const app = createSSRApp(App)  
  const store = createStore()  
  app.use(store)  
  return { app, store }  
}  

Vue 3 uses the __VUE_PROD_SSR__ compilation flag to distinguish environments. Internally, it automatically handles references to client-specific objects like window. For global variable access, it is recommended to use ssrContext injection:

// Set context on the server  
const ctx = {}  
const { app } = createApp()  
renderToString(app, ctx)  

// Access in components  
import { useSSRContext } from 'vue'  
const ctx = inject(SSR_CONTEXT)  

Lifecycle Hook Adjustments

Only specific lifecycle hooks are executed during SSR, requiring special attention:

  • beforeCreate and created run on the server.
  • beforeMount and mounted execute only on the client.
  • beforeUpdate and updated do not run on the server.
  • activated and deactivated require KeepAlive to function.

Async components need special handling:

defineAsyncComponent({  
  loader: () => import('./Component.vue'),  
  suspensible: false // Disable Suspense wrapping  
})  

Template Compilation Differences

During SSR, the compiler generates optimized string-concatenation code. The compileSSR method produces a different render function:

const { compile } = require('vue/compiler-ssr')  
const result = compile(`<div>{{ msg }}</div>`, {  
  isCustomElement: tag => tag.startsWith('x-')  
})  

Key differences include:

  1. Skipping event listener compilation
  2. Omitting virtual DOM patch flags
  3. Hoisting static nodes as string constants
  4. Disabling two-way binding syntax like v-model

Client-Side Hydration Handling

For hybrid rendering, ensure proper client-side hydration. createSSRApp generates special markers:

<div id="app" data-server-rendered="true">  
  <!-- Server-rendered content -->  
</div>  

The hydration process compares existing DOM with virtual DOM. Key logic resides in packages/runtime-core/src/renderer.ts:

if (container._vnode) {  
  // Hydrate existing DOM  
  hydrate(container._vnode, container)  
} else {  
  // Fresh mount  
  render(vnode, container)  
}  

Data Prefetching and State Synchronization

Server-side data prefetching requires special handling to avoid duplicate client requests:

// Server-side router configuration  
router.beforeResolve(async (to, from, next) => {  
  const matched = to.matched.flatMap(record => {  
    return Object.values(record.components).map(component => {  
      return component.asyncData?.({ store, route: to })  
    })  
  })  
  await Promise.all(matched)  
  next()  
})  

// Client-side state synchronization  
if (window.__INITIAL_STATE__) {  
  store.replaceState(window.__INITIAL_STATE__)  
}  

Streaming Rendering

For high-performance scenarios, streaming rendering can be used via renderToStream:

const stream = renderToStream(app)  
stream.on('data', chunk => {  
  res.write(chunk)  
})  
stream.on('end', () => {  
  res.end()  
})  

Streaming requires careful component ordering. Async components must be declared in advance:

defineComponent({  
  ssrPrefetch: true, // Force preloading  
  async setup() {  
    const data = await fetchData()  
    return { data }  
  }  
})  

Cross-Platform API Adaptation

Vue 3 provides platform-agnostic rendering APIs via @vue/server-renderer, implementing basic cross-platform methods:

interface RendererOptions {  
  createElement: (tag: string) => any  
  insert: (el: any, parent: any) => void  
  setElementText: (el: any, text: string) => void  
  patchProp: (el: any, key: string, prevValue: any, nextValue: any) => void  
}  

For specialized environments like mini-programs, these interfaces can be customized.

Error Handling and Fallback Strategies

SSR requires robust error-catching mechanisms:

try {  
  const html = await renderToString(app)  
} catch (err) {  
  // Log errors  
  console.error('SSR error:', err)  
  // Fall back to client-side rendering  
  res.send(`<div id="app"></div>`)  
}  

For third-party library compatibility, mock browser objects with ssrMock:

global.window = {  
  localStorage: {  
    getItem: () => null  
  }  
}  

Performance Optimization Strategies

SSR performance hinges on caching and reuse:

const LRU = require('lru-cache')  
const microCache = new LRU({  
  max: 100,  
  maxAge: 1000 * 60 // 1-minute cache  
})  

function render(req, res) {  
  const cacheKey = req.url  
  if (microCache.has(cacheKey)) {  
    return res.send(microCache.get(cacheKey))  
  }  
    
  renderToString(app).then(html => {  
    microCache.set(cacheKey, html)  
    res.send(html)  
  })  
}  

Component-level caching is achieved via serverCacheKey:

export default {  
  name: 'CachedComponent',  
  props: ['item'],  
  serverCacheKey: props => props.item.id  
}  

Build Configuration Differences

SSR builds require distinct resources:

// vue.config.js  
module.exports = {  
  chainWebpack: config => {  
    config.entry('app').clear().add('./src/entry-client.js')  
      
    config.entry('server').clear().add('./src/entry-server.js')  
  }  
}  

Webpack must handle Node.js modules:

externals: [  
  nodeExternals({  
    allowlist: [/\.css$/, /\?vue&type=style/]  
  })  
]  

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.