简单介绍
使用方法
- 输出load方法用来加载组件,参数为key(模块名)与src(模块脚本)来加载脚本,并返回一个Promise实例,then中回调的参数即为加载的组件。
- 输出output方法用来定义组件,在组件脚本中使用,参数名为一个回调函数,回调函数返回的值为输出的组件。
实现方法
- 设定两个用于缓存数据的内部对象_loadPromises与_loadModules;
- 定义load方法,其中有两个参数key和src,key为模块的唯一标识,src为脚本的路径。
- load中将返回一个Promise实例,并将此Promise实例根据key来缓存到_loadPromises中,并在下次有相同的key时,直接返回此Promise实例。
- 在Promise实例中调用内部的_loadScript(key,src,resolve,reject)方法,其中的resolve和reject分别为Promise的resolve和reject。
- 在_loadScript方法中创建一个HTMLScriptElement实例,在它的onload事件处理程序中调用resolve(_loadModules[key]),在它的onerror事件处理程序中调用reject(message),并设置data-key属性为key,设置src属性为src,再将其添加到body中。
- 定义一个output(callback)供模块脚本中调用,通过document.currentScript元素来获取当前脚本的data-key属性值,并在_loadModules中以此属性值为属性名缓存callback回调函数的执行结果。
补充说明
- 通过将Promise实例的resolve与reject作为值传给其他函数,就可以在其他时间段修改此Promise实例的状态。
- 因为output方法必然在模块脚本中运行,因此可以通过document.currentScript(当前正在运行的脚本元素)来获取模块脚本上添加加的key信息,这一步是至关重要的。因为在组件脚本中只输出value值,而没有定义key值,如果没有key值,将无法确定哪个Promise实例resolve哪个value值。
源码
let _loadPromises: {
[key: string]: Promise<any>
} = {};
let _loadModules: {
[key: string]: any;
} = {};
export function load(key: string, src: string): Promise<any> {
if (isPromise(_loadPromises[key])) {
return _loadPromises[key];
}
let promise = new Promise((resolve, reject) => {
_loadScript(key, src, resolve, reject);
});
_loadPromises[key] = promise;
return promise;
}
function isPromise(promise) {
return Object.prototype.toString.call(promise) === '[object Promise]';
}
function _loadScript(key: string, src: string, resolve: (module: any) => void, reject: (message: string) => void): void {
let script: HTMLScriptElement = document.createElement('script');
script.onload = function () {
resolve(_loadModules[key]);
}
script.onerror = function () {
reject(`加载名为${key}、链接为${src}的模块失败!`);
}
script.src = src;
script.async = true;
script.setAttribute('data-key', key);
document.body.appendChild(script);
}
export function output(callback: () => any): void {
let key = document.currentScript.getAttribute('data-key');
_loadModules[key] = callback();
}
export default { load, output };