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

服务端渲染(SSR)优化策略

作者:陈川 阅读数:6933人阅读 分类: 性能优化

服务端渲染(SSR)优化策略

服务端渲染(SSR)能够提升首屏加载速度,改善SEO效果,但不当实现可能导致服务器压力过大或渲染效率低下。通过合理的缓存策略、代码分割、流式传输等技术手段,可以显著提升SSR应用的性能表现。

静态内容缓存

对于不经常变化的页面内容,使用内存缓存或分布式缓存存储渲染结果。Next.js等框架内置了自动静态优化功能,可通过getStaticProps实现:

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/data')
  const data = await res.json()
  
  return {
    props: { data },
    revalidate: 3600 // 每小时重新生成
  }
}

Redis缓存示例:

async function renderWithCache(req, res) {
  const cacheKey = req.url;
  const cachedHtml = await redis.get(cacheKey);
  
  if (cachedHtml) {
    return res.send(cachedHtml);
  }
  
  const html = await renderToString(<App />);
  await redis.setex(cacheKey, 3600, html); // 缓存1小时
  res.send(html);
}

组件级缓存

针对部分可缓存的组件实施细粒度缓存策略。React的react-ssr-prepass可以预取数据:

import { prepass } from 'react-ssr-prepass';

async function renderApp() {
  const cache = new Map();
  await prepass(<App />, (element) => {
    if (element.type?.fetchData) {
      const promise = element.type.fetchData();
      cache.set(element.type, promise);
    }
  });
  
  const data = await Promise.all([...cache.values()]);
  const html = renderToString(<App />);
  return { html, data };
}

流式渲染

使用renderToNodeStream替代renderToString实现渐进式传输:

import { renderToNodeStream } from 'react-dom/server';

app.get('/stream', (req, res) => {
  res.write('<!DOCTYPE html><html><head><title>Stream</title></head><body><div id="root">');
  const stream = renderToNodeStream(<App />);
  stream.pipe(res, { end: false });
  stream.on('end', () => {
    res.write('</div></body></html>');
    res.end();
  });
});

代码分割与懒加载

结合动态import()实现按需加载:

// 服务端配置
import { ChunkExtractor } from '@loadable/server';

const statsFile = path.resolve('../dist/loadable-stats.json');
const extractor = new ChunkExtractor({ statsFile });

function renderApp() {
  const jsx = extractor.collectChunks(<App />);
  const html = renderToString(jsx);
  const scripts = extractor.getScriptTags();
  
  return { html, scripts };
}

客户端对应配置:

import { loadableReady } from '@loadable/component';

loadableReady(() => {
  hydrateRoot(document.getElementById('root'), <App />);
});

数据预取优化

采用并行数据获取策略减少请求瀑布:

async function fetchAllData(components) {
  const promises = components
    .map(comp => comp.fetchData?.())
    .filter(Boolean);
  
  const results = await Promise.allSettled(promises);
  return results.map(result => 
    result.status === 'fulfilled' ? result.value : null
  );
}

内存管理

避免内存泄漏的全局状态处理方案:

function createAppState() {
  return {
    user: null,
    theme: 'light',
    // 其他状态...
  };
}

app.get('/', (req, res) => {
  const appState = createAppState();
  const html = renderToString(
    <AppContext.Provider value={appState}>
      <App />
    </AppContext.Provider>
  );
  
  // 确保每次请求后清理状态
  appState = null;
});

服务端性能监控

实现渲染性能追踪:

app.use((req, res, next) => {
  const start = process.hrtime();
  
  res.on('finish', () => {
    const duration = process.hrtime(start);
    const ms = duration[0] * 1000 + duration[1] / 1e6;
    metrics.trackRenderTime(req.path, ms);
    
    if (ms > 500) {
      logger.warn(`Slow render detected: ${req.path} took ${ms.toFixed(2)}ms`);
    }
  });
  
  next();
});

构建优化

配置Webpack的SSR专属构建:

module.exports = {
  target: 'node',
  output: {
    libraryTarget: 'commonjs2'
  },
  externals: [nodeExternals()],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: 'null-loader' // 忽略CSS文件
      }
    ]
  }
};

容错处理

确保单个组件错误不影响整体渲染:

function SafeComponent({ children }) {
  try {
    return children();
  } catch (error) {
    console.error('Component render error:', error);
    return null;
  }
}

// 使用方式
<SafeComponent>
  {() => <UnstableComponent />}
</SafeComponent>

混合渲染策略

根据路由动态选择渲染模式:

app.get('*', async (req, res) => {
  const isBot = detectBot(req.headers['user-agent']);
  const needsSEO = ['/about', '/products'].includes(req.path);
  
  if (isBot || needsSEO) {
    // 完整SSR
    const html = await renderToString(<App />);
    res.send(html);
  } else {
    // 客户端渲染
    res.sendFile(path.join(__dirname, 'static/index.html'));
  }
});

服务端资源压缩

启用高效压缩中间件:

const compression = require('compression');
const zlib = require('zlib');

app.use(compression({
  level: zlib.constants.Z_BEST_COMPRESSION,
  threshold: '1kb',
  filter: (req) => !req.path.endsWith('.jpg') // 排除已压缩资源
}));

模板预编译

对于高频使用的模板进行预编译:

const templateCache = new Map();

function compileTemplate(templateName) {
  if (templateCache.has(templateName)) {
    return templateCache.get(templateName);
  }
  
  const template = Handlebars.compile(fs.readFileSync(`templates/${templateName}.hbs`));
  templateCache.set(templateName, template);
  return template;
}

数据库查询优化

减少渲染过程中的数据库查询:

async function getInitialData() {
  // 使用单个复杂查询替代多个简单查询
  const [user, products, notifications] = await Promise.all([
    User.findOne({ id: 123 }),
    Product.find({ featured: true }),
    Notification.count({ unread: true })
  ]);
  
  return { user, products, notifications };
}

服务端渲染降级方案

当SSR失败时自动降级到CSR:

async function renderApp() {
  try {
    const html = await renderToString(<App />);
    return { html, isServerRendered: true };
  } catch (error) {
    console.error('SSR failed, falling back to CSR:', error);
    return { html: '', isServerRendered: false };
  }
}

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

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

前端川

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