ClojureScript

拥抱 JavaScript 工具

2020 年 4 月 24 日
ClojureScript 团队

ClojureScript 1.10.741 包含了一种新的简化方式,用于与现有的 JavaScript 生态系统集成 - bundle target。使用此 target,ClojureScript 编译器的输出可以立即传递给 JavaScript 打包器,例如 WebpackMetro,或任何其他理解 Node.js require 的构建工具。使用此新 target 的 ClojureScript 项目可以自由地集成来自 node_modules 的库,无需手动 externs 或其他配置,同时仍然充分利用 REPL 驱动的开发和高级编译来优化其应用程序的优化部分。

虽然这显然对 ClojureScript 项目的开发便利性产生了重大影响,但我们相信对 ClojureScript 生态系统的益处更为令人兴奋。现在,您可以发布直接依赖于 JavaScript 生态系统的 ClojureScript 库,无需额外仪式,并确信无论他们可能喜欢哪些其他 JavaScript 和 ClojureScript 构建工具,整个社区都将从中受益。

如果您想直接进入并进行教程,请访问 新指南。有关一些历史和背景信息,请继续阅读。

多年来,我们已实施并发布了许多功能来帮助与 JavaScript 生态系统集成,但最终结果实际上感觉更像是各种解决方案的拼凑,而不是完整的整体。其中一部分可能归因于试图绕过现有的 JavaScript 工具,而不是拥抱它们。ClojureScript 在十年前的 Google Closure Compiler 项目的高级编译功能方面投入了大量资金,而且通过它来处理 Node 模块似乎是自然而然的事情。

但是,在我们首次通过 Closure 发布 Node 模块处理后的近三年时间里,很明显,最流行的库中只有少数可以进行高级优化。虽然我们仍然相信这方面有希望,但 ClojureScript 社区必须通过开发可以被流行的 JavaScript 工具轻松使用的引人入胜的 JavaScript 库,同时在使用 ClojureScript 构建时仍然可以进行 Closure 的出色树形摇动和代码拆分,来引领世界。像 Rollup.js 这样的项目已经证明,如果它能带来重大的益处,JavaScript 开发人员并不反对遵守更严格的风格。与此同时,我们需要一种更简单、更容易完成任务的方法。

António Nuno Monteiro 关于 Node 模块处理的最初帖子 中,有一段关于我们在 Node.js 下如何实际为我们知道来自 node_modules 的库生成 Node.js require 语句的简短段落。这是一个绝妙的想法,它在与 Node.js 交互时带来了非常惯用的体验。在接下来的几年里,很明显,使用 ClojureScript 进行 Node.js 开发通常比 Web 开发更简单。现在,bundle target 方法承认了几乎所有现代 JavaScript 依赖项解析都是 Node.js require 或 ES6 import 的事实。

但是,这仅仅解决了一部分问题。我们需要对 ClojureScript 生成的 JavaScript 应用高级优化。这让我们想到了我们称为“externs 推断”的东西。Closure 编译模型的权衡之一是,集成不打算用于 Closure 使用的库需要手动且容易出错的编写 externs 的过程 - 这些文件可以防止 Closure 重命名它实际上不会看到的库的属性和声明。由于 Rich Hickey 早期决定将 ClojureScript 中的全局变量标记为全局变量,以及 Clojure 为本地范围提供了一种简单但有效的类型传播算法,ClojureScript 也实现了这一算法,因此跟踪“外部”值的用法并不像看起来那么困难。

结合我们对 Node.js 和 externs 推断的方法,我们直接得到了 bundle target。当然,现在这一切可能看起来相当明显 - 但权衡各种设计目标可能会很容易掩盖简单的答案。通过将两件截然不同的事情 - 一方面是 ClojureScript,另一方面是 JavaScript 工具 - 实际上允许它们保持独立 - 我们可以实现超越部分之和的东西。同时,这些选择都没有排除将所有内容都传递给 Closure Compiler,如果更多的 JavaScript 库开始采用适合进行积极的死代码消除的代码风格。

此功能是许多讨论以及 ClojureScript 社区中一些出色项目的灵感的结果 - 尤其是 re-natalshadow-cljs

祝您编程愉快!