运行时加载命名空间的顺序与在用户代码中对这些命名空间进行requite操作的顺序无关。这似乎影响了所有优化级别。这意味着开发人员不能依赖在给定命名空间中的副作用代码在依赖于该副作用的另一个命名空间加载之前执行。
复现步骤
运行以下命令
clj --main cljs.main --compile-opts '{:target :nodejs :main a}' --compile a
将会编译在{{a.cljs}}中定义的{{a}}命名空间。该命名空间通过ns宏的{{:require}}形式按字母顺序要求请注明——{{b}}至{{g}}命名空间。
编译后,观察以下情况
例如,在输出文件{{out/cljs_deps.js}}中的{{goog.addDependency}}调用对于{{a.js}}的依赖项按任意顺序。在我的机器上一次运行的结果是
goog.addDependency("../a.js", ['a'], ['cljs.core', 'e', 'c', 'g', 'b', 'd', 'f']);
运行编译后的代码生成的{{node out/main.js}}控制台输出表明运行时加载顺序与{{out/cljs_deps.js}}中反映的顺序相同
{{e}}的主体
{{c}}的主体
{{g}}的主体
{{b}}的主体
{{d}}的主体
{{f}}的主体
分析
看起来确保编译时顺序的工作在[CLJS-1453](
https://dev.clojure.org/jira/browse/CLJS-1453)中作为一部分进行,但事物似乎在{{cljs_deps.js}}生成时变得顺序错了。
看起来给定ClojureScript文件的{{cljs_deps.js}}输出最终由{{cljs.compiler/emit-source}}的输出决定。那里的代码以与CLJS-1453之前的{{cljs.analyzer}}中的'ns'解析方法相同的方式删除了同一组中的重复依赖项。
附带的补丁解决了我在狭窄的应用场景中的问题,但我相信底层函数正将`:uses`和`:requires`作为映射传递,所以可能还需要更多工作才能实现完整的解决方案。