ClojureScript

高级编译

如果你要针对传统的 JavaScript 客户端(网页浏览器),那么从一开始就需要考虑高级编译。否则你必然会遇到一些问题,而这些问题本来可以通过一些提前的准备轻松避免。一般来说,不要等到尝试高级构建时才开始,你应该定期生成生产构建,以便尽早发现问题。

避免使用外部库

最好是简单地避免使用外部库,只要在 Google Closure 库或现有的 ClojureScript 库中存在解决方案即可。外部库必须提供 externs,以便高级编译能够始终如一地工作。

当然,在某些情况下,无法避免使用外部库。

使用外部库

如果你必须使用外部库,请使用经过整理的库,例如 CLJSJS 提供的库。这些库打包了 externs,因此你无需自己提供。

偶尔,你可能会发现自己需要一个没有预先打包的外部库。

提供 externs

当使用一个没有提供 externs 的外部库时,请花时间为你要使用的 API 编写 externs 文件。externs 文件非常容易编写。例如,如果外部库有一个名为 Foo.bar 的属性,你希望访问该属性,那么你的 externs 文件应该包含以下条目

Foo.bar;

如果外部库有一个名为 Foo.baz 的方法,你希望调用该方法,那么你的 externs 文件应该包含以下条目

Foo.baz = function() {};

有时,不会有顶级的 API,而会有一些方法命名约定,即一个临时接口/协议。在这种情况下,使用 Object 定义你的 externs

Object.foo = function() {};
Object.bar = function() {};

当然,有时你可能会错过一个 externs 条目,而生产文件会生成一个难以理解的错误。由于 Closure 编译器的一些选项,这些问题现在不再难于调试。

从 JavaScript 访问

如果你想从 JavaScript 访问 ClojureScript 代码,那么你需要处理这样一个事实,即高级编译会混淆 Var 名字的 JavaScript 表示形式。这可以通过在应该从 JavaScript 访问的 Var 上添加 :export 元数据来轻松解决。

例如,如果你有一个 square 函数,你可以用 ^:export 注释它,如下所示

(ns my-math.core)

(defn ^:export square [x]
  (* x x))

这样,你就可以从 JavaScript 中调用你的 square 函数,如下所示

my_math.core.square(3);

这是通过在生成的 JavaScript 中包含 goog.exportSymbol 调用来实现的,无论是在哪里,:export 元数据都与 Var 相关联。

对于每个导出的 Var,都会建立一个额外的未重命名的别名,它指向 Closure 混淆的名称。混淆的名称在优化的代码中继续被内部使用。

^:export 功能用于(且仅用于)通过未重命名的别名从外部访问 Var。如果你想调试使用缩短名称的优化代码,请考虑使用 :pseudo-names:pretty-print,它们将在下一节中介绍。

你可以单独导出与协议方法关联的 Var。在本例中,barquux 将被导出

(defprotocol IFoo
  (^:export bar [this])
  (baz [this x])
  (^:export quux [this x y]))

修复高级编译问题

将你的生产构建改为使用两个额外的选项 :pseudo-names true:pretty-print true。现在你的错误将显示一个与原始源代码中的名称相对应的名称。为这个遗漏的用例添加一个 externs 条目。

有关 :foreign-libs 编译器选项语法的更多信息,请参见 依赖项