
本文介绍一种安全、模块化的方案,将 filereader 异步解析 excel 后生成的 json 数据(原为局部变量)暴露给其他模块使用,避免污染全局作用域,同时支持 es 模块导入导出。
本文介绍一种安全、模块化的方案,将 filereader 异步解析 excel 后生成的 json 数据(原为局部变量)暴露给其他模块使用,避免污染全局作用域,同时支持 es 模块导入导出。
在前端处理 Excel 文件时,常见模式是通过 <input type="file"> 选择文件,再用 FileReader 异步读取并借助 xlsx 库(如 SheetJS)将其转为 JSON。但问题在于:解析逻辑通常嵌套在事件回调(如 fileReader.onload)中,导致最终数据(如 inputData)仅在闭包内有效,无法直接导出供其他模块使用。
直接声明 var inputData 或 window.inputData 虽能“提升”作用域,但违背模块化设计原则,易引发命名冲突与状态不可控问题。更优解是导出一个可变对象引用——即创建一个顶层常量对象,其属性可被异步逻辑安全更新,其他模块通过导入该对象实时访问最新值。
以下为推荐实现(需确保项目启用 ES 模块,即 <script type="module"></script> 或 .mjs 文件):
// parser.js
let selectedFile;
// ✅ 导出一个带可变属性的对象(非原始值)
export const ExcelData = {
json: null, // 初始值明确,便于类型检查和条件判断
isReady: false
};
document.getElementById('input').addEventListener('change', (event) => {
selectedFile = event.target.files[0];
});
document.getElementById('button').addEventListener('click', () => {
if (!selectedFile) return;
const fileReader = new FileReader();
fileReader.readAsBinaryString(selectedFile);
fileReader.onload = (event) => {
try {
const data = event.target.result;
const workbook = XLSX.read(data, { type: 'binary' });
const sheetName = workbook.SheetNames[0] || 'Grid'; // 更健壮:避免硬编码
const rowObject = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheetName]);
// 去重逻辑优化:使用 Map 提升性能(尤其大数据量)
const seen = new Map();
const uniqueRows = rowObject.filter(item => {
const key = JSON.stringify(item);
if (seen.has(key)) return false;
seen.set(key, true);
return true;
});
// 移除最后一行(按需保留)
uniqueRows.pop();
// ✅ 更新导出对象的属性(非重新赋值!)
ExcelData.json = JSON.stringify(uniqueRows, null, 2);
ExcelData.isReady = true;
console.log('✅ Excel parsed and exported:', ExcelData.json.substring(0, 100) + '...');
} catch (err) {
console.error('❌ Excel parsing failed:', err);
ExcelData.json = null;
ExcelData.isReady = false;
}
};
fileReader.onerror = () => {
console.error('❌ File reading error');
ExcelData.json = null;
ExcelData.isReady = false;
};
});在另一文件中导入并使用:
立即学习“Java免费学习笔记(深入)”;
// main.js
import { ExcelData } from './parser.js';
// 方式1:轮询检查(简单场景)
const checkData = () => {
if (ExcelData.isReady && ExcelData.json) {
console.log('Received JSON:', ExcelData.json);
// ✅ 此处可调用渲染函数、发送 API 等
} else {
setTimeout(checkData, 100); // 避免高频轮询
}
};
// checkData(); // 启动检查
// 方式2:推荐 —— 使用 Promise 封装(更现代、可控)
export function waitForExcelData() {
return new Promise((resolve) => {
const poll = () => {
if (ExcelData.isReady && ExcelData.json) {
resolve(ExcelData.json);
} else {
setTimeout(poll, 50);
}
};
poll();
});
}
// 使用示例
// waitForExcelData().then(json => renderTable(JSON.parse(json)));关键注意事项:
- ❌ 不要
export let inputData = ...并在回调中重新赋值(inputData = ...),这会破坏模块绑定;✅ 必须导出对象并修改其属性。 - ⚠️
JSON.stringify(..., undefined, 4)在生产环境建议改为null缩进(减少体积),格式化应在开发调试时启用。 - ? 异步操作务必添加
try/catch和onerror处理,防止解析失败导致ExcelData.json状态不一致。 - ? 若需服务端共享数据,应配合
localStorage或 IndexedDB 持久化,而非仅依赖内存对象。
此方案兼顾模块化、可维护性与运行时安全性,是处理前端异步数据跨模块共享的实践范式。


















