控制动态加载的脚本上的加载顺序

时间:2016-12-01 16:54:49

标签: javascript coffeescript

我有一个基本的SPA,可以根据需要加载一些资产(主要是样式表和脚本)。

加载器看起来像这样(这是一个简化版本):

class ModuleXLoader
    constructor: ->
        @scripts = [
            'https://www.example.com/assets/js/script1.js',
            'https://www.example.net/assets/js/script2.js',
            'https://www.example.org/assets/js/script3.js'
        ]
        @scriptsLoaded = 0

    load: (@callback) ->
        document.head.appendChild @.scriptTag url for url in @scripts

    scriptTag: (url) ->
        domElement = document.createElement 'script'
        domElement.type = 'text/javascript'
        domElement.onload = (event) =>
            console.log event.currentTarget.src # This logs the script's URL
            @.callback() if ++@scriptsLoaded is @scripts.length and typeof @callback is 'function'
        domElement.src = url
        return domElement

所以,当我需要加载ModuleX时,我会这样做:

loader = new ModuleXLoader()
loader.load () =>
   console.log 'All scripts have been loaded, let\'s do stuff!' 

这会将所需的脚本附加到我的<head>,一切都按预期工作。

当所需脚本之间存在某些依赖关系时,会出现问题。根据每个CDN的响应时间(假设为example.comexample.net ...)脚本以随机顺序加载,因此,有时我得到了经典:

  

未捕获的ReferenceError:未定义 ModuleXDependency

当然,我在我的@scripts数组上尝试了不同的ordenations,但它并不重要。

我正在考虑某种信号量实现:

@scripts =
    script1:
        url: 'https://www.example.com/assets/js/script1.js'
        requires: 'script3'
        loaded: false
    script2: # etc.


domElement.onload = (event) =>
    # This is not a real implementation but kind of pseudocode idea...
    @wait() while not @scripts[@scripts['script1'].requires].loaded

但说实话,走这条路感觉太脏了,所以我想知道是否有人有更好的想法......

2 个答案:

答案 0 :(得分:3)

我的建议是使用像AMD这样的模块定义。也许您应该考虑使用像requirejs这样的现有模块加载器。您还可以在此处找到有关AMD的文档。也许请阅读whyAMD文字。

如果你想要一个absolut最小的加载器并自己加载脚本,你可以结帐loader.js

关于AMD的想法基本上是你定义你的模块:

define('dep', [], function() {
  // here goes your code
  return {};
});

define('mymodule', ['dep'], function(dep) {
  // here goes your code.
});

loader.jsrequire.js这样的加载器基本上会构建这些模块的图形,然后当您执行类似require('mymodule')的操作时,它会知道它必须调用dep首先,然后将结果作为第一个参数注入mymodule

loader.js基本上只会执行此操作,而require.js则可以通过网络加载模块。

然而,两者都可以手动加载脚本标记,等待所有加载,然后调用入口点。

重要的是,执行define的调用的顺序无关紧要。

答案 1 :(得分:1)

一种解决方案是创建捆绑包,只需将您将同时加载的所有依赖项连接到一个缩小的js文件,即&#34;捆绑包&#34;。加载一个文件而不是3.它将解决您的依赖顺序问题,并提供其他改进。