Server-side rendering solution
Server-Side Rendering Solution for ECharts
The server-side rendering (SSR) solution for ECharts primarily addresses the need to generate static content for dynamic charts on the server. Traditional browser-side rendering relies on DOM operations, whereas server environments lack DOM interfaces, requiring specific methods to achieve chart rendering. Below is the complete implementation path and technical details.
Core Implementation Principle
ECharts' server-side rendering relies on libraries like node-canvas
or jsdom
to simulate a browser environment. The underlying process involves the following steps:
- Environment Simulation: Create a virtual Canvas or SVG rendering environment in Node.js.
- Chart Initialization: Initialize the chart instance using the same configuration options as in the browser.
- Data Binding: Inject data into the chart instance.
- Rendering Output: Generate Base64 images or SVG strings.
const { createCanvas } = require('canvas');
const echarts = require('echarts');
// Create a virtual Canvas
const canvas = createCanvas(800, 600);
const chart = echarts.init(canvas);
// Set chart configuration
chart.setOption({
title: { text: 'Server-Side Rendering Example' },
series: [{ type: 'bar', data: [12, 19, 3, 5, 2] }]
});
// Output PNG image
const buffer = canvas.toBuffer('image/png');
fs.writeFileSync('output.png', buffer);
Specific Implementation Solutions
Solution 1: Canvas Rendering
Use the node-canvas
library to simulate a browser Canvas environment, suitable for generating static images:
const { createCanvas } = require('canvas');
const echarts = require('echarts');
function renderChart(options) {
const canvas = createCanvas(options.width || 800, options.height || 600);
const chart = echarts.init(canvas);
chart.setOption(options.config);
return canvas.toDataURL(); // Return DataURL
}
Performance Optimization Tips:
- Reuse Canvas instances to reduce memory overhead.
- Use
offscreen-canvas
for high-concurrency scenarios. - Set appropriate image quality parameters.
Solution 2: SVG Rendering
Use jsdom
to output SVG format, suitable for vector graphics:
const { JSDOM } = require('jsdom');
const echarts = require('echarts');
async function renderSVG(options) {
const dom = new JSDOM(`<!DOCTYPE html><div id="chart"></div>`);
const chart = echarts.init(dom.window.document.getElementById('chart'));
chart.setOption(options);
return chart.renderToSVGString(); // Directly output SVG string
}
Feature Comparison:
Feature | Canvas Solution | SVG Solution |
---|---|---|
Output Format | Bitmap | Vector |
File Size | Larger | Smaller |
Rendering Speed | Fast | Slower |
Scaling Effect | Pixelated | Lossless |
Dynamic Data Rendering
Server-side rendering also supports dynamic data updates. A typical workflow:
// Simulate database query
async function fetchData() {
return {
xAxis: ['Mon', 'Tue', 'Wed'],
series: [Math.random() * 100, Math.random() * 100]
};
}
// Dynamic rendering function
async function renderDynamicChart() {
const data = await fetchData();
const canvas = createCanvas(600, 400);
const chart = echarts.init(canvas);
chart.setOption({
xAxis: { data: data.xAxis },
series: [{ data: data.series }]
});
return canvas.toBuffer();
}
Cluster Rendering Solution
For high-concurrency scenarios, the following architecture is recommended:
Client Request → Load Balancer → Rendering Cluster → Redis Cache → Return Result
Example implementation code:
// Use Koa to build a rendering service
const Koa = require('koa');
const router = require('@koa/router')();
const app = new Koa();
// Add cache middleware
const cache = require('koa-redis-cache');
app.use(cache({
routes: ['/chart'],
expire: 3600
}));
// Chart rendering API
router.get('/chart', async (ctx) => {
const { type = 'png', config } = ctx.query;
const result = await renderService.render(config, type);
ctx.body = result;
});
Error Handling Mechanism
A robust error handling system should include the following layers:
- Parameter Validation:
function validateOptions(options) {
if (!options || typeof options !== 'object') {
throw new Error('Invalid options format');
}
// Additional validation rules...
}
- Rendering Fallback:
try {
const result = await renderChart(options);
if (!result) throw new Error('Empty render result');
return result;
} catch (err) {
console.error(`Render failed: ${err.stack}`);
return fallbackImage; // Return fallback image
}
- Performance Monitoring:
const start = Date.now();
await renderChart(options);
const duration = Date.now() - start;
metrics.timing('chart.render.time', duration);
if (duration > 1000) {
metrics.increment('chart.render.slow');
}
Practical Application Scenarios
Scenario 1: PDF Report Generation
Process for embedding charts in PDF documents:
const { PDFDocument } = require('pdf-lib');
async function generatePDF() {
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage([600, 800]);
// Render chart
const chartImage = await renderChart(salesOptions);
const pngImage = await pdfDoc.embedPng(chartImage);
// Insert into PDF
page.drawImage(pngImage, {
x: 50,
y: 500,
width: 500,
height: 300
});
return pdfDoc.save();
}
Scenario 2: Email Template Embedding
Embed dynamic charts in emails:
const nodemailer = require('nodemailer');
async function sendReportEmail() {
const chartDataURL = await renderChart(emailOptions);
const transporter = nodemailer.createTransport();
await transporter.sendMail({
html: `<img src="${chartDataURL}" alt="Monthly Report">`,
// Other email configurations...
});
}
Performance Optimization Practices
Memory Management
// Explicitly release resources
function disposeChart(chart) {
chart.dispose();
if (chart.getDom()) {
chart.getDom().remove();
}
}
// Use WeakMap to cache instances
const chartCache = new WeakMap();
function getCachedChart(options) {
if (!chartCache.has(options)) {
chartCache.set(options, initChart(options));
}
return chartCache.get(options);
}
Cluster Stress Testing
Use Artillery for load testing:
config:
target: "http://render-service"
phases:
- duration: 60
arrivalRate: 50
scenarios:
- flow:
- get:
url: "/chart?type=svg"
Typical optimization results:
- Average response time reduced from 1200ms to 400ms after warm-up.
- Memory usage reduced by 40% through instance reuse.
- Throughput increased 3x after horizontal scaling.
Version Compatibility Handling
Handling differences between ECharts versions:
function adaptOptionForVersion(option, version) {
if (version.startsWith('4.')) {
// Special handling for v4
delete option.darkMode;
} else if (version.startsWith('5.')) {
// Support for v5 features
option.aria = option.aria || {};
}
return option;
}
Security Measures
- Prevent malicious configurations:
function sanitizeOptions(options) {
// Limit maximum data size
if (options.dataset && options.dataset.source) {
if (options.dataset.source.length > 1000) {
throw new Error('Data exceeds maximum limit');
}
}
// Filter dangerous properties
const forbiddenKeys = ['script', 'javascript'];
Object.keys(options).forEach(key => {
if (forbiddenKeys.includes(key.toLowerCase())) {
delete options[key];
}
});
}
- Rate limiting:
const rateLimit = require('koa-ratelimit');
app.use(rateLimit({
driver: 'redis',
db: redisClient,
duration: 60000,
max: 100
}));
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:SVG与Canvas渲染选择
下一篇:图表导出与打印