控制动态加载脚本的加载顺序
Controlling load order on dynamically loaded scripts
我有一个基本的 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
不过老实说走这条路感觉太脏了,所以我想知道是否有人有更好的主意...
一个解决方案是创建包,只需将您将同时加载的所有依赖项连接到一个缩小的 js 文件中,"bundle"。加载一个文件而不是 3 个文件。它将解决您的依赖顺序问题并提供其他改进。
我的建议是使用像 AMD
这样的模块定义。也许您应该考虑使用现有的模块加载器,例如 requirejs. Here will you also find documentation about AMD
. Maybe read the whyAMD text.
如果你想要一个绝对最小的加载器并自己加载脚本,你可以查看 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
,然后将结果作为第一个参数注入mymodule
。
loader.js
基本上只会这样做,而 require.js
可以通过网络加载模块。
然而,对于这两种方式,您都可以手动加载脚本标签,等待所有脚本标签加载完毕,然后调用您的入口点。
重要的是调用 define
的执行顺序并不重要。
我有一个基本的 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
不过老实说走这条路感觉太脏了,所以我想知道是否有人有更好的主意...
一个解决方案是创建包,只需将您将同时加载的所有依赖项连接到一个缩小的 js 文件中,"bundle"。加载一个文件而不是 3 个文件。它将解决您的依赖顺序问题并提供其他改进。
我的建议是使用像 AMD
这样的模块定义。也许您应该考虑使用现有的模块加载器,例如 requirejs. Here will you also find documentation about AMD
. Maybe read the whyAMD text.
如果你想要一个绝对最小的加载器并自己加载脚本,你可以查看 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
,然后将结果作为第一个参数注入mymodule
。
loader.js
基本上只会这样做,而 require.js
可以通过网络加载模块。
然而,对于这两种方式,您都可以手动加载脚本标签,等待所有脚本标签加载完毕,然后调用您的入口点。
重要的是调用 define
的执行顺序并不重要。