
本文详解如何在 Express + EJS 应用中安全、高效地将服务端数据注入前端 Google Charts,解决 ReferenceError: graphs is not defined 核心问题,涵盖 JSON 序列化、脚本上下文隔离、图表初始化时机等关键实践。
本文详解如何在 express + ejs 应用中安全、高效地将服务端数据注入前端 google charts,解决 `referenceerror: graphs is not defined` 核心问题,涵盖 json 序列化、脚本上下文隔离、图表初始化时机等关键实践。
在基于 Node.js 的 Web 应用中,使用 Google Charts 展示数据库驱动的可视化仪表盘时,常需将服务端准备的数据(如饼图数据集)动态传递至前端 JavaScript 执行环境。但 EJS 模板引擎默认仅渲染 HTML 内容,服务端变量(如 graphs)无法直接被 <script> 中的原生 JS 访问——这正是报错 ReferenceError: graphs is not defined 的根本原因。
✅ 正确做法:在客户端脚本中内联序列化数据
必须通过 EJS 输出语法 <%- JSON.stringify(data) %> 将服务端对象安全嵌入为合法的 JavaScript 字面量。注意使用 <%-(非转义输出)而非 <%=, 因为 JSON.stringify() 生成的是纯 JS 代码,不应被 HTML 转义破坏。
以下是修正后的 chart.ejs 关键部分(已精简冗余结构,修复重复 <html> 和 <body> 标签):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Google Charts + EJS 动态图表</title>
<!-- 加载 Google Charts -->
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<!-- ✅ 关键:在 Google Charts 加载回调前,将 EJS 数据注入为全局 JS 变量 -->
<script type="text/javascript">
// 安全注入服务端数据(自动处理引号、换行等特殊字符)
const graphs = <%- JSON.stringify(graphs) %>;
</script>
<script type="text/javascript">
// 初始化 Google Charts
google.charts.load('current', { packages: ['corechart'] });
google.charts.setOnLoadCallback(drawGraphs);
function drawGraphs() {
console.log('图表数据已加载:', graphs);
// 创建容器 DOM 元素
appendDivToBody(graphs.name, 'width: 100%; max-width: 1200px; height: 500px; margin: 20px auto;');
// 绘制单个饼图
drawGraph(graphs.name, graphs.myData);
}
function drawGraph(graphName, graphData) {
const data = new google.visualization.DataTable();
data.addColumn('string', '品类');
data.addColumn('number', '数量');
data.addRows(graphData); // ✅ 直接使用注入的二维数组
const options = {
title: graphName,
width: 600,
height: 400,
pieHole: 0.3, // 可选:环形图效果
legend: { position: 'right' }
};
const chart = new google.visualization.PieChart(
document.getElementById(graphName)
);
chart.draw(data, options);
}
function appendDivToBody(id, style) {
const div = document.createElement('div');
div.id = id;
div.style.cssText = style;
document.body.appendChild(div);
}
</script>
</head>
<body>
<h1>? 披萨配料销量分析</h1>
<!-- ✅ EJS 渲染 HTML 内容(验证数据可达性) -->
<ul>
<% graphs.myData.forEach(([topping, slices]) => { %>
<li><strong><%= topping %></strong>:售出 <%= slices %> 份</li>
<% }) %>
</ul>
</body>
</html>⚠️ 关键注意事项
- <%- 必须配合 JSON.stringify():<%= graphs %> 会触发 HTML 转义(如将 " 变成 "),导致 JS 语法错误;<%- 确保原始 JSON 字符串原样输出。
- 避免全局污染与作用域混淆:不要在 drawGraphs() 内部重复声明 var graphs = ...,而应在 <script> 顶层一次性注入并声明为 const,确保后续所有函数均可访问。
- HTML 结构合法性:原始模板存在两个 <html> 和 <body> 标签,会导致浏览器解析异常,务必修正为标准单根结构。
- 数据格式一致性:Google Charts 的 addRows() 接受二维数组(如 [['A', 1], ['B', 2]]),EJS 传入的 myData 必须严格匹配此格式(本例中已满足)。
-
错误防御建议:生产环境可添加数据校验:
if (!graphs || !Array.isArray(graphs.myData)) { console.error('图表数据缺失或格式错误:', graphs); return; }
✅ 后端 chart.js 优化提示
- res.render() 中无需指定完整路径 __dirname+"/views/chart.ejs",应配置 Express 视图引擎:
app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs');然后直接调用 res.render('chart', { graphs: myGraph })。
- 若未来扩展为多图表,建议将 graphs 设计为数组(如 [{name: 'Sales', data: [...]}, ...]),前端 drawGraphs() 中使用 graphs.forEach(...) 即可无缝支持。
通过以上改造,服务端数据将可靠注入前端执行上下文,Google Charts 可成功渲染交互式图表——真正实现「数据驱动可视化」的闭环开发体验。










