js/css动态加载器


前言

在我们日常开发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,则不会重复加载资源文件。

这里以加载百度地图的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');
	 ...
})

最后

如果你觉得此文对你有一丁点帮助,麻烦点个赞,鼓励一下。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值