按依赖顺序加载类:自定义Depends实现是否有更标准方案?
问题描述
我希望按依赖顺序加载类,让可扩展类先于继承它的类加载。以下实现方案可以正常工作,但对于这类常见需求来说似乎有些复杂。请问是否存在更标准的实现方式?
当前实现代码
使用方式
const depends = new Depends(LoadDepends); depends.add("markerClass.js", []); depends.add("zcla_clusters.js", ["markerClass.js"]); depends.add("users.js", ["markerClass.js"]); depends.add("zcla_wkcomsh.js", ["markerClass.js"]); depends.chain();
自定义的Depends类
class Depends { /** * 构造函数,创建Depends实例 * @param {Function} onHasDepends - 所有依赖加载完成后执行的回调函数 */ constructor(onHasDepends) { this._depends = []; this._loaded = []; this._loading = []; this._onHasDepends = onHasDepends; } /** * 添加依赖项 * @param {string} name - 依赖项名称 * @param {Array<string>} dependencies - 该依赖项的前置依赖数组 */ add(name, dependencies) { if(!this._depends.includes(name)) { this._depends.push(name) this._depends[name] = dependencies; for(let i in dependencies) { if(!this._depends.includes(dependencies[i])) { alert("依赖 ["+ dependencies[i] +"] 未找到,无法添加 ["+ name +"]"); } } } } /** * 检查指定模块的所有依赖是否已加载 * @param {string} name - 模块名称 * @returns {boolean} - 所有依赖已加载返回true,否则返回false */ depLoaded(name) { let f = true; for (let d = 0; d < this._depends[name].length; d++) { if (!this._loaded.includes(this._depends[name][d])) { f = false; break; } } return f; } /** * 检查所有依赖项是否已加载完成 * @returns {boolean} - 所有依赖已加载返回true,否则返回false */ allLoaded() { let f = true; for (let i = 0; i < this._depends.length; i++) { if (!this._loaded.includes(this._depends[i])) { f = false; break; } } return f; } /** * 启动依赖链加载 */ chain() { const ch = this; for (let i = 0; i < this._depends.length; i++) { if (!this._loading.includes(this._depends[i]) && this.depLoaded(this._depends[i])) { const script = document.createElement('script'); script.src = 'javascripts/' + ch._depends[i]; script.onload = function(){ ch._loaded.push(this.src.split('/').pop()); if (ch.allLoaded()) { ch._onHasDepends(); } else { ch.chain(); } }; ch._loading.push(ch._depends[i]); document.head.appendChild(script); } } } }
标准实现方案
1. 使用ES原生模块(推荐)
现代浏览器和Node.js都原生支持ES模块,完全不需要自定义依赖加载逻辑,步骤如下:
- 在HTML中加载入口脚本时添加
type="module"属性:
<script type="module" src="javascripts/main.js"></script>
- 在各个JS文件中用
import和export声明依赖关系:
比如markerClass.js作为基类:
export default class MarkerClass { // 类实现 }
zcla_clusters.js中导入依赖并实现子类:
import MarkerClass from './markerClass.js'; export default class ZclaClusters extends MarkerClass { // 子类实现 }
users.js同理:
import MarkerClass from './markerClass.js'; export default class Users extends MarkerClass { // 类实现 }
最后在入口文件main.js中导入所有需要的模块,浏览器会自动按依赖顺序加载:
import ZclaClusters from './zcla_clusters.js'; import Users from './users.js'; import ZclaWkcomsh from './zcla_wkcomsh.js'; // 对应原来的LoadDepends函数,所有模块加载完成后执行初始化 function LoadDepends() { // 初始化操作 } LoadDepends();
ES模块会自动处理依赖解析、顺序加载,还支持异步加载、树摇等特性,是目前最标准的方案。
2. 使用模块打包工具(适合生产环境)
如果需要兼容旧浏览器或做打包优化,可使用Webpack、Rollup、Vite这类工具:
- 代码编写依然用ES模块语法
- 配置打包工具将所有模块打包成单个或多个bundle文件
- 最终在HTML中引入打包后的文件即可,工具会自动处理依赖顺序和兼容性
3. 原生动态import()(按需加载场景)
如果需要按需加载模块,可使用原生import()函数,它返回Promise,能灵活控制加载顺序:
// 先加载基础依赖 import('./markerClass.js') .then(() => { // 依赖加载完成后,并行加载所有依赖它的模块 return Promise.all([ import('./zcla_clusters.js'), import('./users.js'), import('./zcla_wkcomsh.js') ]); }) .then(() => { // 所有模块加载完成,执行回调 LoadDepends(); }) .catch(err => console.error('加载失败:', err));
这种方式无需自定义类,利用原生Promise就能实现依赖顺序控制,代码更简洁。
内容的提问来源于stack exchange,提问作者Simon Barnett
相关产品推荐
相关产品推荐

