脚本的动态加载

课后整理 2020-12-20

除了静态的<script>标签,还可以动态生成<script>标签,然后加入页面,从而实现脚本的动态加载。例如:

['a.js',  'b.js'].forEach(function(src) {
    var  script = document.createElement('script');
    script.src = src;
    document.head.appendChild(script);
});

这种方法的好处是,动态生成的<script>标签不会阻塞页面渲染,也就不会造成浏览器假死。但是问题在于,这种方法无法保证脚本的执行顺序,哪个脚本文件先下载完成,就先执行哪个。

如果想避免这个问题,可以设置async属性为false。例如:

['a.js',  'b.js'].forEach(function(src) {
    var  script = document.createElement('script');
    script.src = src;
    script.async = false;
    document.head.appendChild(script);
});

上面的代码依然不会阻塞页面渲染,而且可以保证b.js在a.js后面执行。不过需要注意的是,在这段代码后面加载的脚本文件,会因此都等待b.js执行完成后再执行。

把上面的写法封装成一个函数。

(function  () {
    var scripts =  document.getElementsByTagName('script')[0];
    function load(url) {
        var script = document.createElement('script');
        script.async = true;
        script.src = url;
        scripts.parentNode.insertBefore(script,  scripts);
    }
    load('//apis.google.com/js/plusone.js');
    load('//platform.twitter.com/widgets.js');
    load('//s.thirdpartywidget.com/widget.js');
}());

在上面代码中,async属性设为true,是因为加载的脚本没有互相依赖关系。而且,这样就不会造成堵塞。

如果想为动态加载的脚本指定回调函数,可以使用下面的写法。例如:

function  loadScript(src, done) {
    var js = document.createElement('script');
    js.src = src;
    js.onload = function () {
        done();
    };
    js.onerror = function () {
        done(new Error('加载失败:' + src));
    };
    document.head.appendChild(js);
}

此外,动态嵌入还有一个地方需要注意。动态嵌入必须等待CSS文件加载完成后,才会去下载外部脚本文件。静态加载就不存在这个问题,<script>标签指定的外部脚本文件,都是与CSS文件同时并发下载的。