如何用类实现外部jQuery库加载执行?解决fetch调用报错问题
问题描述
尝试编写类来加载remodal.js、datatables.js等外部jQuery库并执行逻辑,但类版本的loadFile方法抛出错误:
VM1903:10 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'map')
at eval (eval at (script.js:10:21), :10:4806)
at eval (eval at (script.js:10:21), :10:148)
at eval (eval at (script.js:10:21), :10:171)
at script.js:10:21
此前普通函数版本可正常运行,代码如下:
正常运行的函数版代码
JavaScript
const sources = [ { path: 'https://cdnjs.cloudflare.com/ajax/libs/remodal/1.1.1/remodal.min.js', callback: null }, { path: 'https://cdn.datatables.net/1.12.1/js/jquery.dataTables.min.js', callback: () => { document.getElementById('example').DataTable(); } }, ] sources.forEach(source => loadFile(source)); function loadFile(source) { fetch(source.path) .then(data => data.text()) .then(text => eval(text)) .then(() => source.callback); }
HTML
<script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script> <a id="remodalOn" href="#modal">Click me!</a> <div class="remodal" data-remodal-id="modal"> <button data-remodal-action="close" class="remodal-close">Close modal</button> <h1>Remodal Test</h1> <table id="example"> <thead> <tr> <th>Given name</th> <th>Surname</th> <th>Age</th> </tr> </thead> <tbody> <tr> <td>Harry</td> <td>Potter</td> <td>17</td> </tr> <tr> <td>Lily</td> <td>Potter</td> <td>40</td> </tr> </tbody> </table> </div>
报错的类版代码
JavaScript
class ExternalJSfile { constructor(path, callback = null) { this.path = path; this.callback = callback; } loadFile() { fetch(this.path) .then(data => data.text()) .then(text => eval(text)) .then(() => this.callback); } } // Load remodal.js const remodal = new ExternalJSfile( 'https://cdnjs.cloudflare.com/ajax/libs/remodal/1.1.1/remodal.min.js' ); remodal.loadFile(); // Load datatables.js and then execute .DataTable() const dataTables = new ExternalJSfile( 'https://cdn.datatables.net/1.12.1/js/jquery.dataTables.min.js', () => { document.getElementById('example').DataTable() } ); dataTables.loadFile();
HTML
<script src="https://code.jquery.com/jquery-3.6.1.min.js" integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script> <a id="remodalOn" href="#modal">Click me!</a> <div class="remodal" data-remodal-id="modal"> <button data-remodal-action="close" class="remodal-close">Close modal</button> <h1>Remodal Test</h1> <table id="example"> <thead> <tr> <th>Given name</th> <th>Surname</th> <th>Age</th> </tr> </thead> <tbody> <tr> <td>Harry</td> <td>Potter</td> <td>17</td> </tr> <tr> <td>Lily</td> <td>Potter</td> <td>40</td> </tr> </tbody> </table> </div>
问题分析与解决方案
错误原因
- 作用域问题:类方法中的
eval默认在方法局部作用域执行,外部库无法正确挂载到全局对象(比如jQuery的$),导致库内部调用map等方法时找不到依赖对象。 - 回调未执行:代码中仅引用了回调函数,没有实际调用(
then(() => this.callback)应该改为执行函数)。 - 并行加载冲突:类版同时触发两个库的加载,可能导致依赖jQuery的库在未完全初始化时就执行逻辑。
正确的类实现方案
核心改进点
- 让
eval在全局作用域执行,确保库挂载到window。 - 修复回调执行逻辑。
- 支持顺序加载,避免依赖冲突。
修正后的类代码
class ExternalJSfile { constructor(path, callback = null) { this.path = path; this.callback = callback; } async loadFile() { try { const response = await fetch(this.path); const text = await response.text(); // 强制在全局作用域执行脚本,确保库正确挂载 (0, eval)(text); // 执行回调(存在时) this.callback?.(); } catch (error) { console.error(`加载文件失败: ${this.path}`, error); } } // 静态方法:按顺序加载多个文件,处理依赖关系 static async loadSequentially(files) { for (const file of files) { const loader = new ExternalJSfile(file.path, file.callback); await loader.loadFile(); } } } // 推荐:顺序加载(避免依赖冲突) const sources = [ { path: 'https://cdnjs.cloudflare.com/ajax/libs/remodal/1.1.1/remodal.min.js' }, { path: 'https://cdn.datatables.net/1.12.1/js/jquery.dataTables.min.js', callback: () => { document.getElementById('example').DataTable(); } } ]; ExternalJSfile.loadSequentially(sources);
说明
(0, eval)(text)是将eval转为全局作用域执行的技巧,确保外部库能正确绑定到window对象。- 使用
async/await替代链式then,代码更简洁,也方便实现顺序加载。 - 增加错误捕获,便于调试加载失败场景。
- 静态方法
loadSequentially支持按顺序加载多个文件,适合有依赖关系的库。
内容的提问来源于stack exchange,提问作者Miu
相关产品推荐
相关产品推荐

