前言
在我们日常开发vue或react项目时,多多少少会遇到这样的需求:
我想在某个页面加载一个外部的资源文件,例如百度地图的js文件,还要在确保文件加载完成之后,执行一些逻辑。这些第三方文件,往往只在一个场景用到,并不想放在全局,这时候就需要实现一个文件的动态加载器。
代码
话不多说,先上代码:
// 如果没有传入type,那么根据url的后缀来确定type
const getTypeFromUrl = url => {
const _type = /\.[^.]+$/.exec(url)[0];
if (_type === '.js') {
return 'text/javascript';
}
if (_type === '.css') {
return 'text/css';
}
}
// 格式化传入的文件列表
const formatFileList = file => {
let list;
if (typeof file === 'string') {
list = [file];
} else if (Array.isArray(file)) {
list = file;
} else {
throw {error: 'The parameter must be a string or an array'};
}
let formatList;
formatList = list.map((i) => {
let url;
let type;
if (typeof i === 'string') {
url = i;
type = getTypeFromUrl(url);
} else if (Array.isArray(i)) {
[url] = i;
type = (i[1] && i[1].type) ? i[1].type : getTypeFromUrl(url);
} else {
throw {error: 'The parameter must be a string or an array'};
}
if (!type) {
throw {error: 'The URL type is not known'};
}
return {url, type};
});
return formatList;
}
// 根据类型创建元素
const createElement = ({type, url}) => {
switch (type) {
case 'text/javascript': {
const element = document.createElement('script');
element.setAttribute('type', 'text/javascript');
element.src = url;
return element;
}
case 'text/css': {
const element = document.createElement('link');
element.href = url;
element.setAttribute('rel', 'stylesheet');
element.setAttribute('media', 'all');
element.setAttribute('type', 'text/css');
return element;
}
default:
break;
}
}
// 加载文件
const loadElement = element => new Promise((resolve) => {
const head = document.getElementsByTagName('head')[0];
head.appendChild(element);
if (element.readyState) {
element.onreadystatechange = () => {
if (element.readyState === 'loaded' || element.readyState === 'complete') {
element.onreadystatechange = null;
resolve();
}
};
} else {
element.onload = resolve;
}
});
const dynamicFile = async file => {
try {
const list = formatFileList(file);
const elementList = list.map(createElement);
await Promise.all(elementList.map((i) => {
return loadElement(i);
}));
} catch (error) {
console.log(error);
}
}
// 单例防止重复加载
const createSingle = fn => {
const instanceMap = {};
return function () {
return instanceMap[arguments[1]] || (instanceMap[arguments[1]] = fn.apply(this, arguments));
}
}
export default createSingle(dynamicFile);
如何使用
方法传入一个数组和一个string类型的name,name相当于一个标识,如果name相同,本页面加载过后,如果别的页面也有相同的name,则不会重复加载资源文件。
这里以加载百度地图的js为例,像这种url后缀不明确,无法得知是js还是css,所以需要手动指定type,如果url后缀是.js或.css,则可以省略这个参数。
dynamicImport([
['http://api.map.baidu.com/api?v=2.0&ak=VcIKGv3f9eq712B7idam46dd6in2fg8R&callback=baiduMap', {type: 'text/javascript'}],
['http://api.map.baidu.com/getscript?v=2.0&ak=VcIKGv3f9eq712B7idam46dd6in2fg8R&services=&t=20210225162129', {type: 'text/javascript'}]
], 'baiduMap').then(() => {
let map = new BMap.Map('myMap');
let point = new BMap.Point(116.397128, 39.916527);
map.centerAndZoom(point, 6);
map.enableScrollWheelZoom();
})
如果只有一个文件,且后缀为.js或.css,可以简写为:
dynamicImport('https://cdn.bootcdn.net/ajax/libs/Chart.js/3.7.0/chart.min.js', 'chart').then(() => {
const ctx = document.getElementById('myChart').getContext('2d');
...
})
结尾
我是周小羊,一个前端萌新,写文章是为了记录自己日常工作遇到的问题和学习的内容,提升自己,如果您觉得本文对你有用的话,麻烦点个赞鼓励一下哟~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。