服务端渲染(SSR)优化策略
服务端渲染(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