我有一个基本的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.com
、 example.net
...)脚本以随机顺序加载,因此,有时我会得到经典的:
Uncaught ReferenceError: ModuleXDependency is not defined
当然,我在我的 @scripts
上尝试了不同的排序。数组,但这并不重要。
我正在考虑某种信号量实现:
@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
但说实话,这样走感觉太脏了,所以我想知道是否有人有更好的主意......
最佳答案
我的建议是使用像 AMD
这样的模块定义。也许您应该考虑使用现有的模块加载器,例如 requirejs 。您还可以在这里找到有关 AMD
的文档。也许阅读 whyAMD文本。
如果您想要一个绝对最小的加载程序并自己加载脚本,您可以查看 loader.js
.
AMD 的想法基本上是这样定义模块:
define('dep', [], function() {
// here goes your code
return {};
});
define('mymodule', ['dep'], function(dep) {
// here goes your code.
});
像 loader.js
或 require.js
这样的加载器基本上会构建这些模块的图表,然后当你执行类似 require('mymodule ')
它会知道必须先调用 dep
,然后将结果作为第一个参数注入(inject)到 mymodule
中。
loader.js
基本上只会执行此操作,而 require.js
具有通过网络加载模块的能力。
但是,对于这两种方法,您都可以手动加载脚本标记,等到所有标记都加载完毕,然后调用您的入口点。
重要的是,对 define
的调用执行顺序并不重要。
关于javascript - 控制动态加载脚本的加载顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40916204/