- 前端项目中使用到了一个报表库
Plotly.js
, 这个库有 600多k。由于报表只有部分模块用到,所以想使用动态加载方式。
- 首先想到使用
webpack
的懒加载,但是打包时间太长。加这个库之前 30秒,加之后 6 分钟。使用 noParse
也没有效果。
- 所以决定用到时,手动加载。
- js 常用的动态加载有两种。
ajax 加载后使用 eval 执行
。或者使用 script 标签加载
。
- 这里介绍动态创建标签的方法。不说了,直接上代码:
// Attach handlers for all browsers
var loadScript = function (path, callback) {
const me = this;
const script = document.createElement('script');
script.type = 'text/javascript';
let done = false;
const head = document.head;
const error = () => {
if (!done) {
done = true;
script.onload = script.onreadystatechange = script.onerror = null;
head.removeChild(script);
callback(false);
}
}
const success = () => {
if (!done) {
done = true;
script.onload = script.onreadystatechange = script.onerror = null;
head.removeChild(script);
callback(true);
}
}
script.onreadystatechange = function () {
if (this.readyState == "loaded" || this.readyState == "complete") {
success();
}
};
script.onload = success;
script.onerror = error;
script.src = path;
head.appendChild(script);
}
- 上面是动态加载
js
的方法。但是可能多个模块会用到这个 js
库。当同时打开这些模块时,有可能会导致多次加载。所以可以把加载后的模块根据路径缓存起来。
- 下面代码是使用
TypeScript
写的, 根据路径记录 js 文件加载信息,避免重复:
export default class LoadJs {
public static loadSync(path: string): Promise<void> {
const me = this;
return new Promise((resolve, reject) => {
me.load(path, (bSuc) => {
if (bSuc) {
resolve();
} else {
reject();
}
});
});
}
public static load(path: string, callback: (bSuc: boolean) => void): void {
let lib = this.pathLoaded[path];
// 没有记录,添加记录
if (!lib) {
lib = {
isLoaded: false,
callback: [callback],
};
this.pathLoaded[path] = lib;
}
// 已加载直接返回
if (lib.isLoaded) {
callback(true);
} else {
// 添加回调
lib.callback.push(callback);
// 加载
const me = this;
this.loadScript(path, suc => {
if (suc) {
me.onLoad(lib, true);
} else {
me.onLoad(lib, false);
}
})
}
}
private static loadScript(path, callback) {
const me = this;
const script = document.createElement('script') as any;
script.type = 'text/javascript';
let done = false;
const head = document.head;
const error = () => {
if (!done) {
done = true;
script.onload = script.onreadystatechange = script.onerror = null;
head.removeChild(script);
callback(false);
}
}
const success = () => {
if (!done) {
done = true;
script.onload = script.onreadystatechange = script.onerror = null;
head.removeChild(script);
callback(true);
}
}
script.onreadystatechange = function () {
if (this.readyState == "loaded" || this.readyState == "complete") {
success();
}
};
script.onload = success;
script.onerror = error;
script.src = path;
head.appendChild(script);
}
private static pathLoaded: { [key: string]: PathLoading } = {};
private static onLoad(p: PathLoading, isSuc: boolean): void {
p.isLoaded = true;
for (const fun of p.callback) {
fun(isSuc);
}
p.callback = [];
}
}
interface PathLoading {
isLoaded: boolean;
callback: Array<(f: boolean) => void>;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。