ClojureScript

使用 Webpack 的 ClojureScript

本指南需要 ClojureScript 1.10.741 或更新版本,並假設您已熟悉快速開始.

此頁面說明如何將 ClojureScript 與典型的 JavaScript 打包器(例如Webpack)整合。您應該已安裝Node.js。本指南假設您已閱讀過快速開始。本指南大量借鑒了這篇關於 Webpack 2 的優秀指南

雖然我們在這裡碰巧使用 Webpack,但這些說明很容易適用於其他打包器,例如用於 React Native 的 Metro。

設定您的專案

首先建立一個專案目錄

mkdir hello-bundler
cd hello-bundler

建立一個具有以下內容的 deps.edn 檔案

{:deps {org.clojure/clojurescript {:mvn/version "1.10.741"}}}

建立一個空的 package.json 檔案

echo "{}" > package.json

新增 webpack 及其命令列工具

npm install --save-dev webpack webpack-cli

我們現在準備好設定我們的 JS 相依性。

JavaScript 相依性

安裝 react 和 react-dom

npm install --save react react-dom

現在使用以下內容建立 src/hello_bundler/core.cljs

(ns hello-bundler.core
  (:require [react]))

(.log js/console react/Component)

請注意,我們正在像正常的 require 和正常的命名空間一樣 require React。

為了使這能正常運作,我們需要設定幾個編譯器選項。建立一個具有以下內容的 build.edn 檔案

{:main hello-bundler.core
 :output-to "out/index.js"
 :output-dir "out"
 :target :bundle
 :bundle-cmd {:none ["npx" "webpack" "./out/index.js" "-o" "out" "--mode=development"]
              :default ["npx" "webpack" "./out/index.js" "-o" "out"]}
 :closure-defines {cljs.core/*global* "window"}} ;; needed for advanced

我們的建置將產生 out/index.js,這正是 Webpack 正在尋找的入口檔案。我們會將打包器的結果寫回輸出目錄。

請注意新的 :target :bundle 選項。這確保了產生的程式碼與可以處理 Node.js 樣式的 require 的常用 JavaScript 打包器相容。它還設定了一堆其他合理的預設值,例如外部推斷,因此進階編譯將可以正常運作。:bundle-cmd 只是在 ClojureScript 建置完成後要執行的任意 shell 命令。在使用像 Metro 這樣的監看打包器的情況下,您可能不會費心使用 :bundle-cmd

讓我們看看實際效果,以下操作將建置您的專案,然後啟動 REPL

clj -M -m cljs.main -co build.edn -v -c -r

您的預設瀏覽器將開啟 https://127.0.0.1:9000。開啟開發人員主控台,您應該會看到已記錄 React.Component

在 REPL 中,您可以 require react 並與之互動

user> (require 'react)

覆寫外部函式庫

您可能會發現您想要從 node_modules 使用 React,可能是因為最新的打包版本尚未出現在 CLJSJS 上。但是,您仍然想使用一些 ClojureScript React 綁定,例如 Reagent。ClojureScript 開箱即用地支援此功能。

為了示範這一點,請將您的 deps.edn 變更為以下內容

{:deps {org.clojure/clojurescript {:mvn/version "1.10.741"}
        reagent {:mvn/version "0.10.0" :exclusions [cljsjs/react cljsjs/react-dom]}}}

將您的原始程式碼檔案變更為以下內容

(ns hello-bundler.core
  (:require [goog.dom :as gdom]
            [reagent.dom :as dom]))

(defn simple-component []
  [:div
   [:p "I am a component!"]
   [:p.someclass
    "I have " [:strong "bold"]
    [:span {:style {:color "red"}} " and red "] "text."]])

(dom/render [simple-component] (gdom/getElement "app"))

重新建置您的專案,執行 REPL

clj -M -m cljs.main -co build.edn -v -c -r

為了驗證外部推斷允許進階編譯運作,讓我們進行進階建置。REPL 在進階編譯下無法運作,因此您必須手動開啟 https://127.0.0.1:9000

clj -M -m cljs.main -co build.edn -O advanced -v -c -s

就這樣!

建置 Web Workers

使用 :target :bundle 建置 web workers 時,您可以使用 webpack(或您偏好的打包器)來新增 web worker 啟動程式碼。

因此,您建置的 :target 仍然是 :bundle(而不是 :webworker),但您會告訴您的打包器建置一個 web worker。例如,使用 webpack,您可以將 --target=webworker 引數新增到您的 :bundle-cmd 項目。

您還需要將 cljs.core/__global__ 定義為 "self"(而不是瀏覽器建置中的 "window")。

一個範例 build-webworker.edn 可能看起來像

{:main hello-bundler.webworker
 :output-to "out/worker/index.js"
 :output-dir "out/worker"
 :target :bundle
 :bundle-cmd {:none ["npx" "webpack" "out/worker/index.js" "-o" "out/worker/main.js" "--target=webworker" "--mode=development"]
              :default ["npx" "webpack" "out/worker/index.js" "-o" "out/worker/main.js" "--target=webworker"]}
 :closure-defines {cljs.core/*global* "self"}} ;; needed for advanced

原始作者:David Nolen