js 脚本懒加载,类似于webpack import()方法,可以适用于静态js文件的加载
脚本文件
type FetchSuccess = {
success: boolean;
loading: boolean;
name: string;
script: any;
};
// 加载脚本
// name 名字,用来判断是否加载过
// src 地址
// timeOut 超时时间
const fetchScript = (() => {
const fetchSuccessList: FetchSuccess[] = [];
return function fetchFn(
name: string,
src: string,
timeOut: number = 5000,
): Promise<{ success: boolean }> {
return new Promise(resolve => {
const isCaching = fetchSuccessList.find(item => item.name === name);
if (isCaching === undefined) {
fetchSuccessList.push({ name, loading: true, success: false, script: null });
}
const current = fetchSuccessList.find(item => item.name === name) as any;
// 代表之前已经获取过,就直接返回成功
if (current.success) {
resolve({ success: true });
return;
}
if (current.script) {
/**
* 这里可能并发调用这个函数,同时调用三次,第一个先进来创建 script,
* 后面的就不应该在创建 script,
* 避免重复创建script, 等待第一次 onload
*/
// setTimeout 实现
function callback() {
return new Promise(newResolve => {
setTimeout(() => {
// 超时
if (new Date().getTime() - current.time >= timeOut) {
newResolve();
return;
}
// loading 结束
if (current.loading === false) {
newResolve();
return;
}
// 这里需要递归执行自己进行等待,直到前面两个断言进去后才执行最外层的 resolve
callback().then(() => callback());
}, 50);
}).then(() => {
resolve({ success: current.success });
});
}
callback();
// setInterval 实现
// new Promise(newResolve => {
// const timer = setInterval(() => {
// // 超时
// if (new Date().getTime() - current.time >= timeOut){
// newResolve(timer);
// return;
// }
// // loading 结束
// if (current.loading === false) {
// newResolve(timer);
// return;
// }
// }, 50);
// }).then((timer: any) => {
// clearInterval(timer);
// resolve({ success: current.success });
// });
return;
}
current.script = document.createElement('script');
current.loading = true;
current.time = new Date().getTime();
const { script } = current;
script.src = src;
document.body.appendChild(script);
console.time(name + ':脚本加载时间');
script.onload = () => {
console.timeEnd(name + ':脚本加载时间');
current.success = true;
current.loading = false;
resolve({ success: true });
};
script.onerror = () => {
console.timeEnd(name + ':脚本加载时间');
document.body.removeChild(script);
current.script = null;
current.success = false;
current.loading = false;
resolve({ success: false });
};
});
};
})();
export default fetchScript;
使用方式,封装echarts高阶组件,echarts从props里面取,统一管理echarts的加载方式,也可以用webpack
import()替换,webpack的需要增加包下载和打包时间和修改publicPath
import React, { PureComponent } from 'react';
import fetchScript from '@/util/fetchScript';
const echartsUrl = 'xxx'; // 你的静态文件地址
const echartsHoc = Component => {
return class Index extends PureComponent {
static echarts = null;
state = {
echarts: Index.echarts,
};
async componentDidMount() {
// 防止多 setState 一次
if (Index.echarts) {
return;
}
const { success } = await fetchScript('echarts', echartsUrl);
if (!success) {
return;
}
Index.echarts = window.echarts || null;
this.setState({
echarts: Index.echarts,
});
}
render() {
const { echarts } = this.state;
if (echarts === null) {
return null;
}
return <Component {...this.props} echarts={echarts} />;
}
};
};
export default echartsHoc;
网友评论