shadow-cljs是新一代的cljs打包工具,比起以前的cljsbuild和figwheel的优势主要是:

  1. 支持直接引用npm依赖, 以前还需要使用cljsjs转一次,不仅麻烦而且还是过时的
  2. 和webpack类似的代码分割, 依赖

当然,cljs+css代码重载是最基本的,而且构建速度也足够快

基于这些特点,打算采用shadow-cljs来编译cljs 想法是好的,实际运行的发现现实总是骨感的…

1. 安装shadow-cljs

我先创建了一个luminus的应用,然后按照shadow-cljs官网所说,直接安装跑起来

1
2
3
4
lein new luminus shadow-gen
cd shadow-gen
yarn add shadow-cljs
shadow-cljs init

2. shadow-cljs打包配置

然后我创建一个Build配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
;; shadow-cljs configuration
{:source-paths
 ["src/cljs" "env/dev/cljs/"]
 :dependencies
 [[org.clojure/clojurescript "1.9.946"]
  [re-frame "0.10.5"]
  [reagent "0.7.0"]
  [secretary "1.2.3"]
  [cljs-ajax "0.7.3"]
  [day8.re-frame/re-frame-10x "0.2.0"]]
 :verbose true
 :builds
 {:app {:target :browser
        :output-dir "target/cljsbuild/public/js"
        :asset-path "/js"
        :dev
        {:compiler-options {:closure-warnings {:global-this :off}
                            :closure-defines {"re_frame.trace.trace_enabled_QMARK_" true}
                            :source-map true
                            :optimizations :none
                            :pretty-print true}}
        :release
        {:compiler-options {:optimizations :advanced
                            :pretty-print false
                            :closure-warnings
                           {:externs-validation :off :non-standard-jsdoc :off}}}
        :modules {:app {:entries [shadow-gen.app]}}
        :devtools {:after-load shadow-gen.core/mount-components
                   :preloads [day8.re-frame-10x.preload]
                   }
        }}
 }

开了day8.re-frame-10x.preload半天页面一直崩,问了下作者thheller发现reframe的dev工具有bug,现在还无解… 然后lein repl,start之后,访问一下localhost:3000, 一开始是好的,后来改下代码hmr之后发现果然崩了。。

打开app.cljs一看,由于figwheel-no-load元数据, figwheel每次重载都会避开app.cljs,

1
2
3
4
5
6
7
8
9
(ns ^:figwheel-no-load shadow-gen.app
  (:require [shadow-gen.core :as core]
            [devtools.core :as devtools]))

;; shadow-gen自带了devtool不需要这些了
;; (enable-console-print!)
;; (devtools/install!)

(core/init!)

shadow-gen不会skip,每次都会reload整个ns,然后init!的时候会重复创建goog.History对象,就崩了, 跟作者提了下,作者当天就把这个功能加上去了。。。

后来请教了下赵宇少侠,发现把core.cljs里的init!导出,然后在html引的时候直接调用core里的init也是一种很好的解决方案, 然后把entry改成core.cljs,每次重载的时候调用edn里配置的:after-load去mount就好了

1
2
3
4
5
6
    {% script "/js/app.js" %}
    <script type="text/javascript">
     var context = "{{servlet-context}}";
     var csrfToken = "{{csrf-token}}";
     shadow_gen.core.init_BANG_(); // 加了这行
    </script>

3. 整合leiningen

这样打包基本就ok了,怎么和lein这个呢,我的代码里既有java代码,又有clj和cljs代码 一开始我是先编译cljs的

shadow-cljs release app

然后发现leiningen在编译的时候,直接会清空target目录… 翻了下leiningen文档,发现在pretask里可以指定任务顺序,我的是先编译java,然后编译clj,最后使用shadow.cljs.devtools.cli编译cljs lein里面javac可以编译java,compile编译clj, 去掉原来的:cljsbuild最后配置是这样

1
2
3
4
5
6
7
{:uberjar {:omit-source true
             :main ^:skip-aot shadow-gen.core
             :prep-tasks ["javac" "compile" ["run" "-m" "shadow.cljs.devtools.cli" "release" "app"]]
             :aot :all
             :uberjar-name "shadow-gen.jar"
             :source-paths ["env/prod/clj"]
             :resource-paths ["env/prod/resources"]}}

这样就大功告成了! 成功把shadow-cljs和leiningen整合,完美java,clj,cljs混编!