
本文介绍一种安全、模块化的方案,将事件驱动下生成的 Excel 解析结果(如 inputData)从局部作用域暴露为可被其他 JS 文件导入的共享数据,避免使用污染全局命名空间的 var 或 window 属性。
本文介绍一种安全、模块化的方案,将事件驱动下生成的 excel 解析结果(如 `inputdata`)从局部作用域暴露为可被其他 js 文件导入的共享数据,避免使用污染全局命名空间的 `var` 或 `window` 属性。
在前端处理 Excel 文件(如通过 SheetJS / xlsx 库)时,解析逻辑通常嵌套在异步事件回调中(如 FileReader.onload),导致解析结果(例如 inputData)天然处于函数局部作用域,无法直接 export。强行提升至全局(如 window.inputData)不仅违背模块化原则,还易引发竞态与覆盖风险。
推荐做法是:导出一个带可变属性的对象(Object Reference),而非原始值本身。该对象在模块顶层定义并导出,其属性可在后续任意时机被安全赋值——因为导入方始终引用同一内存地址,能实时获取最新值。
以下为优化后的完整实现(假设文件名为 excelParser.js):
// excelParser.js
import * as XLSX from 'xlsx';
// ✅ 顶层声明:导出一个可变对象引用
export const ExcelData = {
inputData: null, // 初始值设为 null,明确表示“尚未解析”
isLoading: false, // 可选:增加状态标识,便于消费方判断
};
document.getElementById('input').addEventListener('change', (event) => {
ExcelData.selectedFile = event.target.files[0];
});
document.getElementById('button').addEventListener('click', () => {
const { selectedFile } = ExcelData;
if (!selectedFile) return;
ExcelData.isLoading = true;
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]);
// 去重:基于 JSON 字符串化(适用于结构简单、无函数/undefined 的数据)
const uniqueData = rowObject.filter((item, index) =>
index === rowObject.findIndex(obj => JSON.stringify(obj) === JSON.stringify(item))
);
// 移除最后一项(按需)
uniqueData.pop();
// ✅ 关键:直接赋值给导出对象的属性
ExcelData.inputData = JSON.stringify(uniqueData, null, 2);
ExcelData.isLoading = false;
console.log('✅ Excel parsed and stored:', ExcelData.inputData.substring(0, 100) + '...');
} catch (err) {
console.error('❌ Excel parsing failed:', err);
ExcelData.isLoading = false;
}
};
fileReader.onerror = () => {
console.error('❌ File reading error');
ExcelData.isLoading = false;
};
});
在另一文件(如 dataConsumer.js)中,可直接导入并监听或使用:
// dataConsumer.js
import { ExcelData } from './excelParser.js';
// 方式1:轮询检查(简单场景)
const checkData = () => {
if (ExcelData.inputData) {
console.log('Received data:', JSON.parse(ExcelData.inputData));
} else if (ExcelData.isLoading) {
setTimeout(checkData, 100); // 短暂延迟后重试
}
};
// 方式2:使用 Promise 封装(推荐,更现代)
export function waitForExcelData() {
return new Promise((resolve) => {
const poll = () => {
if (ExcelData.inputData) resolve(JSON.parse(ExcelData.inputData));
else if (ExcelData.isLoading) setTimeout(poll, 50);
else resolve(null); // 超时或失败
};
poll();
});
}
// 使用示例
waitForExcelData().then(data => {
if (data) renderTable(data); // 自定义渲染逻辑
});
⚠️ 重要注意事项:
-
不要用
export let inputData = ...:因变量在事件回调中才赋值,导入方初始化时得到的是undefined,且后续重新赋值不会更新导入绑定(ESM 绑定是只读的)。 -
避免
window.inputData:破坏模块封装,难以测试,易与其他脚本冲突。 -
去重逻辑局限性:
JSON.stringify比较适用于纯数据对象;若含Date、RegExp或方法,需改用深比较库(如lodash.isEqual)或自定义序列化。 -
错误处理必须添加:
FileReader和XLSX.read()均可能抛错,未捕获将导致静默失败。 -
类型安全建议:在 TypeScript 项目中,可为
ExcelData添加接口定义,提升开发体验。
此方案兼顾模块化、可维护性与运行时安全性,是处理“异步生成、跨文件共享”数据的经典模式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











