阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 服务端渲染(SSR)优化

服务端渲染(SSR)优化

作者:陈川 阅读数:47718人阅读 分类: 构建工具

性能优化与服务端渲染(SSR)的关系

服务端渲染(SSR)的核心目标是提升首屏加载性能和SEO友好性,但不当的实现方式反而可能导致性能下降。Vite.js的SSR实现通过独特的架构设计,在开发体验和运行时性能之间取得了平衡。与传统打包工具不同,Vite的SSR构建利用了浏览器原生ES模块系统,避免了不必要的打包开销。

Vite SSR基础架构分析

Vite的SSR实现分为两个独立构建阶段:

  1. 客户端构建:生成静态资源和客户端入口
  2. 服务端构建:生成SSR专用的入口模块

典型项目结构示例:

├── src
│   ├── main.js          # 通用入口
│   ├── entry-client.js  # 客户端入口
│   └── entry-server.js  # 服务端入口
├── index.html
└── vite.config.js

关键配置示例:

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

关键性能优化策略

依赖外部化处理

将node_modules中的依赖标记为external可显著提升SSR构建速度:

// vite.config.js
export default defineConfig({
  ssr: {
    noExternal: ['需要打包的依赖'],
    external: ['应外部化的依赖']
  }
})

组件级代码分割

利用动态导入实现组件级懒加载:

// 服务端入口
export default async (url) => {
  const { createApp } = await import('./main.js')
  const { router } = createApp()
  
  await router.push(url)
  await router.isReady()
  
  return renderToString(app)
}

流式渲染优化

通过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())
})

缓存策略实现

页面级缓存

基于路由的缓存实现示例:

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

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)
  })
})

组件级缓存

Vue的组件缓存配置:

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

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

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

内存管理优化

内存泄漏预防

SSR常见的内存泄漏场景处理:

// 确保每个请求有独立的应用实例
const createApp = () => {
  const app = express()
  app.use('*', async (req, res) => {
    const vueApp = createVueApp() // 每个请求新建实例
    try {
      const html = await renderToString(vueApp)
      res.send(html)
    } finally {
      // 清理工作
      vueApp.unmount()
    }
  })
  return app
}

大JSON序列化优化

使用devalue替代JSON.stringify:

import devalue from 'devalue'

const state = { /* 大型状态对象 */ }
const serialized = devalue(state)
const html = `
  <script>window.__INITIAL_STATE__ = ${serialized}</script>
`

构建时优化技巧

预编译静态内容

将静态部分提前编译为字符串:

// 构建脚本
import { renderToString } from 'vue/server-renderer'
import staticComponent from './StaticComponent.vue'

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

// 服务端使用
const finalHTML = `
  ${staticHTML}
  ${dynamicHTML}
`

多进程渲染

利用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)
})

监控与性能指标

SSR性能指标收集

关键指标监控实现:

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

错误边界处理

组件级错误捕获:

// ErrorBoundary.vue
export default {
  errorCaptured(err) {
    sendErrorToMonitoring(err)
    return false // 阻止错误继续向上传播
  }
}

生产环境特定优化

基于压力的降级策略

在服务器高负载时降级为CSR:

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

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

智能预加载策略

根据用户行为预测预加载:

// 在布局组件中
onMounted(() => {
  const links = [...document.querySelectorAll('a[href]')]
  links.forEach(link => {
    link.addEventListener('mouseenter', () => {
      if (isLikelyNextPage(link)) {
        prefetchSSRBundle(link.href)
      }
    }, { once: true })
  })
})

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

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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌