阿里云主机折上折
  • 微信号
Current Site:Index > Server-side rendering (SSR) optimization

Server-side rendering (SSR) optimization

Author:Chuan Chen 阅读数:47719人阅读 分类: 构建工具

The Relationship Between Performance Optimization and Server-Side Rendering (SSR)

The core objectives of Server-Side Rendering (SSR) are to improve first-screen loading performance and SEO friendliness. However, improper implementation can lead to performance degradation. Vite.js's SSR implementation achieves a balance between development experience and runtime performance through its unique architecture design. Unlike traditional bundlers, Vite's SSR build leverages the browser's native ES module system, avoiding unnecessary bundling overhead.

Analysis of Vite SSR's Basic Architecture

Vite's SSR implementation is divided into two independent build stages:

  1. Client-side build: Generates static resources and client entry points.
  2. Server-side build: Generates SSR-specific entry modules.

Example of a typical project structure:

├── src
│   ├── main.js          # Universal entry
│   ├── entry-client.js  # Client entry
│   └── entry-server.js  # Server entry
├── index.html
└── vite.config.js

Key configuration example:

// vite.config.js
export default defineConfig({
  build: {
    ssr: true,
    rollupOptions: {
      input: 'src/entry-server.js'
    }
  }
})

Key Performance Optimization Strategies

Dependency Externalization

Marking dependencies in node_modules as external can significantly improve SSR build speed:

// vite.config.js
export default defineConfig({
  ssr: {
    noExternal: ['Dependencies to bundle'],
    external: ['Dependencies to externalize']
  }
})

Component-Level Code Splitting

Implement lazy loading at the component level using dynamic imports:

// Server entry
export default async (url) => {
  const { createApp } = await import('./main.js')
  const { router } = createApp()
  
  await router.push(url)
  await router.isReady()
  
  return renderToString(app)
}

Streaming Rendering Optimization

Achieve progressive rendering with pipeToNodeStream:

import { renderToNodeStream } from 'vue/server-renderer'

app.use('*', (req, res) => {
  const stream = renderToNodeStream(app)
  stream.pipe(res, { end: false })
  stream.on('end', () => res.end())
})

Cache Strategy Implementation

Page-Level Caching

Example of route-based caching:

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

app.get('*', (req, res) => {
  const hit = microCache.get(req.url)
  if (hit) return res.end(hit)
  
  renderPage(req).then(html => {
    microCache.set(req.url, html)
    res.end(html)
  })
})

Component-Level Caching

Vue component caching configuration:

// server.js
const { createServerRenderer } = require('vite')
const createCache = require('lru-cache')

const componentCache = new createCache({
  max: 1000,
  maxAge: 1000 * 60 * 15 // 15 minutes
})

const renderer = await createServerRenderer({
  componentCacheKey: (component) => {
    return component.type.__file || component.type.name
  },
  componentCache
})

Memory Management Optimization

Memory Leak Prevention

Handling common SSR memory leak scenarios:

// Ensure each request has an independent app instance
const createApp = () => {
  const app = express()
  app.use('*', async (req, res) => {
    const vueApp = createVueApp() // Create a new instance per request
    try {
      const html = await renderToString(vueApp)
      res.send(html)
    } finally {
      // Cleanup
      vueApp.unmount()
    }
  })
  return app
}

Large JSON Serialization Optimization

Use devalue instead of JSON.stringify:

import devalue from 'devalue'

const state = { /* Large state object */ }
const serialized = devalue(state)
const html = `
  <script>window.__INITIAL_STATE__ = ${serialized}</script>
`

Build-Time Optimization Techniques

Precompiling Static Content

Precompile static parts into strings:

// Build script
import { renderToString } from 'vue/server-renderer'
import staticComponent from './StaticComponent.vue'

const staticHTML = renderToString(staticComponent)
fs.writeFileSync('dist/static.html', staticHTML)

// Server-side usage
const finalHTML = `
  ${staticHTML}
  ${dynamicHTML}
`

Multi-Process Rendering

Parallel rendering using worker_threads:

import { Worker } from 'worker_threads'

function renderInWorker(url) {
  return new Promise((resolve) => {
    const worker = new Worker('./render-worker.js', {
      workerData: { url }
    })
    worker.on('message', resolve)
  })
}

// render-worker.js
const { parentPort, workerData } = require('worker_threads')
renderPage(workerData.url).then(html => {
  parentPort.postMessage(html)
})

Monitoring and Performance Metrics

SSR Performance Metrics Collection

Key metrics monitoring implementation:

app.use((req, res, next) => {
  const start = Date.now()
  res.on('finish', () => {
    const duration = Date.now() - start
    metrics.track('ssr_render_time', duration)
  })
  next()
})

Error Boundary Handling

Component-level error capturing:

// ErrorBoundary.vue
export default {
  errorCaptured(err) {
    sendErrorToMonitoring(err)
    return false // Prevent error from propagating further
  }
}

Production-Specific Optimizations

Load-Based Degradation Strategy

Fallback to CSR when server is under high load:

const shouldDegrade = () => {
  return process.memoryUsage().rss > 500 * 1024 * 1024 // 500MB
}

app.use('*', (req, res) => {
  if (shouldDegrade()) {
    return res.sendFile('dist/client/index.html')
  }
  // Normal SSR processing
})

Intelligent Preloading Strategy

Predictive preloading based on user behavior:

// In layout components
onMounted(() => {
  const links = [...document.querySelectorAll('a[href]')]
  links.forEach(link => {
    link.addEventListener('mouseenter', () => {
      if (isLikelyNextPage(link)) {
        prefetchSSRBundle(link.href)
      }
    }, { once: true })
  })
})

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

如果侵犯了你的权益请来信告知我们删除。邮箱: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 ☕.