如何优雅的实现js资源的按顺序加载

740 阅读1分钟

在实际的开发过程中,我们有时候会遇到动态加载静态资源的需要,这当然是一个很简单的需求,但是如何做到更优雅的实现呢?

参考优秀的vue-ueditor-wrap

首先,我们可以采用回调函数的方法实现

function loadScript(link, cb){
    if(typeof cb !== 'function'){
        cb = function(){}
    }
    const script = document.createElement('script');
    srcipt.src = link;
    script.onload = cb;
    script.onerror = reject;
    document.getElementsByTagName('head')[0].appendChild(script);
}

loadScript('XXX.js', loadScript('X1.js', /*...*/ ))

上面的方法,如果依赖的js比较多,写出来的代码就会很丑。

通过Promise来实现,会让我们的代码看起来更加优雅

var cache = {}
function loadScript(link){
    if(cache[link]) return Promise.resolve()
    return new Promise((resolve, reject)=>{
        function success(){
            cache[link] = true
            resolve()
        }
        function fail(){
            reject(link)
        }
        const script = document.createElement('script');
        srcipt.src = link;
        script.onload = success;
        script.onerror = fail;
        document.getElementsByTagName('head')[0].appendChild(script);
    })
}

实现加载资源的函数

// 依次加载依赖的 JS 文件
// 通过reduce将要加载的js通过promise链串联起来
// 动态创建 script 是先加载完的先执行,所以不可以一次性创建所有资源的引入脚本
// 根据promise的异常传透原理, 如果有一个加载错误,后面的js就不再加载,并将错误的js连接直接抛出
function asyncSeries(funs){
     return funs.reduce((promise, fun) => promise.then(fun), Promise.resolve());
}
function loadStaticSource(links:[]){
    return new Promise((resolve, reject)=>{
        asyncSeries(jsLinks.map((link) => () => loadScript(link)))
        .then(resolve)
        .catch(reject);
    })
}

测试一下,效果不错!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        var cache = {}
        function loadScript(link) {
            if (cache[link]) return Promise.resolve()
            return new Promise((resolve, reject) => {
                function success() {
                    cache[link] = true
                    resolve()
                }
                function fail() {
                    reject(link)
                }
                const script = document.createElement('script');
                script.src = link;
                script.onload = success;
                script.onerror = fail;
                document.getElementsByTagName('head')[0].appendChild(script);
            })
        }
        function asyncSeries(funs) {
            return funs.reduce((promise, fun) => promise.then(fun), Promise.resolve());
        }
        function loadStaticSource(links) {
            return new Promise((resolve, reject) => {
                let p = asyncSeries(links.map((link) => () => loadScript(link)))
                
                p.then(resolve)
                    .catch(reject);
            })
        }
        const links = ['https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js', 'yy.js', 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js'];

        const links1 = ['https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js', 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js']
        
        loadStaticSource(links).then(() => {
            console.log("资源加载完成")
        }, (link) => {
            console.log(link + "资源加载错误")
        })

        loadStaticSource(links1).then(() => {
            console.log("资源加载完成")
        }, (link) => {
            console.log(link + "资源加载错误")
        })
    </script>
</body>

</html>