ClojureScript

ClojureScript 与 Webpack

本指南要求使用 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 和命名空间一样要求 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 中,您可以要求 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 工作线程

使用 :target :bundle 构建 Web 工作线程时,您使用 webpack(或您喜欢的打包器)添加 Web 工作线程引导程序。

因此,您的构建的 :target 仍然是 :bundle(而不是 :webworker),但您将告诉您的打包器构建一个 Web 工作线程。例如,使用 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