命名空间在运行时加载的顺序与用户代码中命名空间被requird的顺序无关。这似乎会影响所有优化级别。这意味着开发人员不能依赖于具有副作用代码的给定命名空间在依赖于该副作用的另一个命名空间加载之前执行。
重现步骤
运行以下命令:
clj --main cljs.main --compile-opts '{:target :nodejs :main a}' --compile a
将编译在{{a.cljs}}中定义的{{a}}命名空间。该命名空间通过ns宏的{{:require}}形式按字母顺序需要命名空间{{b}}至{{g}}。
编译后,观察以下情况:
在输出文件{{out/cljs_deps.js}}中,对于{{a.js}}的{{goog.addDependency}}调用包含其依赖项的任意顺序。例如,在我的机器上的一次运行产生了以下结果:
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`,因此可能需要对完整的解决方案进行更多工作。