您现在的位置是:网站首页 > 服务端渲染方案文章详情
服务端渲染方案
陈川
【
ECharts
】
42447人已围观
6072字
服务端渲染方案
ECharts 作为一款强大的数据可视化库,通常运行在浏览器环境中。但在某些场景下,比如需要 SEO 优化、首屏加载速度提升或静态内容生成时,服务端渲染(SSR)成为必要选择。ECharts 提供了多种服务端渲染方案,能够满足不同技术栈的需求。
基于 Node.js 的渲染方案
Node.js 环境下可以通过 node-canvas
或 echarts-node
实现服务端渲染。核心思路是模拟浏览器环境,生成图表图像或 SVG 字符串。
const echarts = require('echarts');
const { createCanvas } = require('canvas');
// 创建虚拟 canvas
const canvas = createCanvas(800, 600);
const chart = echarts.init(canvas);
// 设置配置项
chart.setOption({
title: { text: '服务端渲染示例' },
series: [{
type: 'bar',
data: [12, 19, 3, 5, 2, 3]
}]
});
// 获取 PNG 图像
const buffer = canvas.toBuffer('image/png');
require('fs').writeFileSync('output.png', buffer);
这种方案需要注意字体问题,中文可能需要额外配置:
const { registerFont } = require('canvas');
registerFont('path/to/simhei.ttf', { family: 'SimHei' });
// 在 option 中指定字体
chart.setOption({
textStyle: {
fontFamily: 'SimHei'
}
});
基于 Puppeteer 的无头浏览器方案
对于复杂图表或需要精确渲染的场景,可以使用 Puppeteer 控制真实浏览器进行渲染:
const puppeteer = require('puppeteer');
const fs = require('fs');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// 加载本地 HTML 模板
await page.setContent(`
<!DOCTYPE html>
<div id="chart" style="width:800px;height:600px;"></div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script>
const chart = echarts.init(document.getElementById('chart'));
chart.setOption(${JSON.stringify({
series: [{ type: 'pie', data: [{value:335, name:'直接访问'}] }]
})});
</script>
`);
// 等待渲染完成
await page.waitForTimeout(500);
// 截图保存
const element = await page.$('#chart');
await element.screenshot({ path: 'chart.png' });
await browser.close();
})();
服务端 SVG 生成方案
ECharts 5+ 版本支持直接导出 SVG:
const echarts = require('echarts');
// 初始化虚拟节点
const container = document.createElement('div');
const chart = echarts.init(container, null, {
renderer: 'svg',
width: 800,
height: 600
});
chart.setOption({
xAxis: { type: 'category', data: ['Mon', 'Tue'] },
yAxis: { type: 'value' },
series: [{ data: [820, 932], type: 'line' }]
});
// 获取 SVG 字符串
const svgStr = chart.renderToSVGString();
与前端框架的集成方案
Next.js 集成示例
在 Next.js 的 getServerSideProps
中渲染图表:
export async function getServerSideProps() {
const { createCanvas } = require('canvas');
const echarts = require('echarts');
const canvas = createCanvas(800, 600);
const chart = echarts.init(canvas);
chart.setOption({
series: [{
type: 'scatter',
data: [[10, 5], [20, 20]]
}]
});
return {
props: {
chartData: canvas.toDataURL()
}
};
}
// 页面组件中使用
function Page({ chartData }) {
return <img src={chartData} alt="服务端渲染图表" />;
}
Nuxt.js 集成方案
创建 Nuxt 服务器中间件:
// server/middleware/chart.js
const { createCanvas } = require('canvas');
const echarts = require('echarts');
export default function(req, res) {
const canvas = createCanvas(400, 300);
const chart = echarts.init(canvas);
chart.setOption({
series: [{ type: 'bar', data: [5, 20, 36, 10] }]
});
res.setHeader('Content-Type', 'image/png');
res.end(canvas.toBuffer());
}
性能优化策略
- 缓存渲染结果:对相同配置的图表进行缓存
const chartCache = new Map();
function renderChart(options) {
const cacheKey = JSON.stringify(options);
if (chartCache.has(cacheKey)) {
return chartCache.get(cacheKey);
}
// ...渲染逻辑
chartCache.set(cacheKey, result);
return result;
}
- 使用 Worker 线程:避免阻塞主线程
const { Worker } = require('worker_threads');
function renderInWorker(options) {
return new Promise((resolve) => {
const worker = new Worker(`
const { parentPort } = require('worker_threads');
const echarts = require('echarts');
const { createCanvas } = require('canvas');
parentPort.on('message', (options) => {
const canvas = createCanvas(800, 600);
const chart = echarts.init(canvas);
chart.setOption(options);
parentPort.postMessage(canvas.toBuffer());
});
`, { eval: true });
worker.on('message', resolve);
worker.postMessage(options);
});
}
- 按需加载 ECharts 组件:
// 只加载需要的图表组件
const echarts = require('echarts/core');
const { BarChart } = require('echarts/charts');
const { CanvasRenderer } = require('echarts/renderers');
echarts.use([BarChart, CanvasRenderer]);
常见问题解决方案
字体显示异常:确保服务端有相应字体文件
// 使用系统字体或注册自定义字体
const { registerFont } = require('canvas');
registerFont('/path/to/font.ttf', { family: 'Custom Font' });
图表交互问题:服务端渲染后保留交互能力
<div id="chart-container">
<!-- 服务端渲染的占位图 -->
<img src="/chart.png" alt="预览图" />
</div>
<script>
// 客户端水合
fetch('/chart-config.json')
.then(res => res.json())
.then(option => {
const chart = echarts.init(document.getElementById('chart-container'));
chart.setOption(option);
});
</script>
大图表内存问题:分批渲染超大图表
function renderLargeChart(options) {
const partialOptions = {
...options,
series: options.series.slice(0, 10) // 先渲染部分数据
};
// 后续通过 WebSocket 或 AJAX 加载剩余数据
}
高级应用场景
PDF 报表生成:将 ECharts 渲染到 PDF 文档
const { PDFDocument } = require('pdf-lib');
const fs = require('fs');
async function generatePDF() {
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([800, 600]);
// 渲染图表到 PNG
const pngImage = await pdfDoc.embedPng(chartBuffer);
page.drawImage(pngImage, { x: 0, y: 0 });
fs.writeFileSync('report.pdf', await pdfDoc.save());
}
邮件嵌入图表:直接将 SVG 嵌入 HTML 邮件
const svgString = chart.renderToSVGString();
const emailHtml = `
<div>
<h1>每日报表</h1>
${svgString}
</div>
`;
命令行输出:在终端显示图表
const { termImage } = require('term-img');
function renderInTerminal() {
const buffer = chartCanvas.toBuffer();
console.log(termImage(buffer, { width: '50%' }));
}
上一篇: SVG与Canvas渲染选择
下一篇: 图表导出与打印