ClojureScript

ClojureScript 不是孤島:整合 Node 模組

2017 年 7 月 12 日
António Nuno Monteiro

這是搶先預覽系列的第二篇文章。

自 2011 年首次發布以來,ClojureScript 就擁有第一級的 JavaScript 互通性。與 Clojure 類似,擁抱主機一直是明確的目標。雖然從語法角度來看以上陳述是正確的,但與外部 JavaScript 函式庫整合在過去需要一些手動工作 [1]手動組裝的捆綁包或由社群努力所領導的封裝)。

朝向與外部 JavaScript 更好的互動邁進一步

2015 年 ClojureScript 的 Google Summer of Code 專案成功地簡化了與外部 JavaScript 模組的互動。依靠Google Closure Compiler當時理解大多數廣為人知的 JavaScript 模組格式並將其轉換為 Closure 相容 JavaScript 的新能力[2]Maria Geller成功地將這些模組功能整合到 ClojureScript 中,同時還有自訂預處理步驟,允許從您的 ClojureScript 專案中舒適地使用諸如流行的 JavaScript 編譯技術Babel等功能[3]

自那時起,Closure Compiler 團隊在 2016 年增加了模組解析支援,這在其他方面能夠直接從 node_modules 安裝中使用模組,而不必為轉換提供 Closure 手工製作的模組路徑。

與 NPM 相依性的無縫互動

我們以 Maria 的模組工作為基礎,考慮到這個新的 Closure 功能,ClojureScript 的下一個版本代表了另一個重要的里程碑,它透過大幅改進 ClojureScript 與 NPM 生態系統的互通方式,使每個 ClojureScript 專案都能存取任意 JavaScript 模組。

自 ClojureScript 版本 1.9.518 以來,從 ClojureScript 命名空間要求 NPM 模組已成為可能。然而,Node.js 支援多種模式來要求 CommonJS 模組,而常見的痛點是人們希望從 ClojureScript 中要求 "react-dom/server" 形式的模組,這對 :require 規格來說不是有效的符號。

在這個版本中,我們在命名空間形式中加入了對基於字串的要求的支援,以解決上述問題。您現在可以從您的 ns 宣告中舒適地要求這些類型的模組。

將它們結合在一起的是 :npm-deps 編譯器旗標。在其中,我們告訴編譯器它應該知道哪些相依性。ClojureScript 將負責安裝這些相依性並將它們通過 Closure Compiler 轉換管道執行,包括我們在下面更詳細描述的優化。

一個實際範例

給定一個如下所示的 build.clj 檔案

(require '[cljs.build.api :as b])

(b/build "src"
  {:output-dir "out"
   :output-to "out/main.js"
   :optimizations :none
   :main 'example.core
   :install-deps true
   :npm-deps {:react "15.6.1"
              :react-dom "15.6.1"}})

您最簡單的 src/example/core.cljs 檔案可能看起來像下面的程式碼片段

(ns example.core
  (:require [react :refer [createElement]]
            ["react-dom/server" :as ReactDOMServer :refer [renderToString]]))

(js/console.log (renderToString (createElement "div" nil "Hello World!")))

請注意,我們不必在任何地方宣告 "react-dom/server"。我們可以要求它。ClojureScript 現在足夠聰明,可以找到這些 CommonJS 模組並將它們處理成與 Google Closure Compiler 相容的程式碼。

這是一件大事

使用 Google Closure 消費 JavaScript 模組的含義是巨大的:ClojureScript 專案中使用的外部函式庫不再只是被添加到生成的捆綁包的前面,而是現在可以受到所有 Closure Compiler 的優化,包括無效程式碼消除,以及在利用程式碼分割的專案中,跨模組程式碼移動。例如,在我們的測試中,在 Closure 的進階編譯下,React 比使用現有的流行 JavaScript 工具小得多(約 16%)[4]。此外,如果您有一個 ClojureScript 和 JavaScript 的混合程式碼庫,您不僅可以無縫地使用程式碼的那些 JavaScript 部分(包括例如 JSX 轉換!),還可以與您的 ClojureScript 部分使用的那些程式碼共享和捆綁它們的供應商相依性。

也適用於 Node.js!

值得注意的是,ClojureScript 中的模組處理功能主要旨在用於以瀏覽器為目標的專案,其中相依性通常會捆綁在一起。但這並不意味著以 Node.js 為目標的專案也不能利用此功能。事實上,我們使其也能夠在以 Node.js 為目標時,從您的命名空間宣告中無縫地要求本地 node_modules 安裝中的 Node 模組。ClojureScript 將知道您正在要求 Node 模組並產生 require 宣告,與 Node.js 自己的用於載入 JavaScript 模組的設施整合。

最後的想法

經過將近 6 年,ClojureScript 是一個受到全球大量開發人員信賴的平台,我們希望繼續實現與主機的完全互通性。透過確保我們與廣大的 JavaScript 生態系統整合,我們認為即將在下一個 ClojureScript 版本中推出的這些新功能是確保 ClojureScript 長期可持續性的一個墊腳石。

我們希望您像我們一樣喜歡這些新功能。感謝您的閱讀!


1. 除了自 ClojureScript 初始發布以來就深度整合的Google Closure Library
2. Closure Compiler 需要執行諸如無效程式碼消除(樹狀結構搖動)、變數名稱重寫、常數摺疊和內聯等優化的 JavaScript 子集。
3. Maria 在該專案工作期間也持續寫部落格,您可以在這裡閱讀。
4. 事實上,Reagent 團隊已經在測試其網站的版本來消費 NPM 模組。他們還將其與先前版本進行比較