仓库 https://github.com/thheller/shadow-cljs
npm https://www.npmjs.com/package/shadow-cljs
shadow-cljs 是一个 ClojureScript 编译器的封装,
和 lein-cljsbuild + lein-figwheel 或者 boot-cljs + lein-reload 功能类似.
它能够提供对于浏览器平台和 Node.js 平台代码编译, 优化, 热替换.
这个项目是最近才冒出来的, 近期做了不少的修改, 名字也定下来不久.
作者是 Thomas Heller https://twitter.com/thheller 在 Clojurians 群里好像一直很活跃,
作者经常说: 我在等你们试用然后提 issue…
优点
shadow-cljs 相对于以往的方案有两个优点, 主要针对 JavaScript 开发者:
- 通过 npm 安装
npm i shadow-cljs
系统里安装了 npm 的同学都可能很快安装上 shadow-cljs,
我建议在项目根路径安装, 然后通过 npm script 调用, 目前这是 js 社区常用的玩法.
JVM 被工具内部管理了, 不需要复杂的配置, 尽管功能会局限一点.
如果系统已经安装 JVM 会直接用, 没有的话估计要全局装一下 node-jre
模块.
- 生成 CommonJS 代码
跟以往的编译器不一样的是, shadow-cljs 支持生成 CommonJS 格式的代码,
我们知道 ClojureScript 用了 Closure Compiler 的命名空间的, 跟 Node 不兼容,
而 shadow-cljs 做的是强行在代码当中插入了 CommonJS 的 require/exports 代码,
总体上已经能做到正常地放进 Webpack 打包, 以及用 node 命令直接运行编译结果.
用法
前几周更新比较频繁, 可能后面细节会调整, 这里基于 0.9.4
介绍.
- 编译到 CommonJS
shadow-cljs 的配置文件是 EDN 文件, 大致结构是:
{:dependencies []
:source-paths ["src"]
:builds {:app {:target :npm-module
:output-dir "compiled/"}}}
这里定义了一个编译策略叫做 app
, 命令行当中就可以使用这个配置:
shadow-cljs --build app --once
这个命令就会把 src/
目录下的 cljs 文件统一编译到 compiled/
目录下面.
其中 :npm-module
这个选项设定了编译结果是 npm 模块用的文件.
除了 --once
, 还有两个常用的命令:
shadow-cljs --build app --dev # 监视文件修改自动编译
shadow-cljs --build app --release # 使用 :advanced 模式编译优化代码
在 --dev
模式下, 文件类似增量编译, 可以被 Webpack 监听到,
通过这个方式, CommonJS 文件可以和 Webpack 比较好地配合使用.
可以翻阅我写的例子 https://github.com/minimal-xyz/minimal-shadow-cljs-commonjs
配合 Webpack 热替换的例子 https://github.com/minimal-xyz/minimal-shadow-cljs-webpack
也可以看 Wiki https://github.com/thheller/shadow-cljs/wiki/ClojureScript-for-JS-Devs
- 编译到浏览器
如果是编译到浏览器, 作者提供的 demo 是这样的:
{:dependencies []
:source-paths ["src"]
:builds {:app {:target :browser
:output-dir "public/js"
:asset-path "/js"
:modules {:main [my.app]}}}}
如果要开启代码热替换, 需要配置 :devtools
选项激活热替换的代码:
{:target :browser
; ...
:devtools {:before-load my.app/stop
:after-load my.app/start}}
激活热替换之后, 还可以启动 REPL 用来发送 cljs 到浏览器执行.
:modules
配置用来指定程序启动的入口的文件, 前面 npm 模块里用不到这个,
其他还提供了一些相对高级的策略比如分包, 用来处理处理浏览器环境的细节,
具体看 Wiki https://github.com/thheller/shadow-cljs/wiki/ClojureScript-for-the-browser
不过我倾向还是用 Webpack 来处理奇怪的场景, 毕竟 js 这边套路比较多.
用 Webpack 的时候会注意到 SourceMaps 并不是立即生效的,
需要在 Webpack 配置里读取已有的信息, 继续生成 SourceMaps,
虽然体验不是完美, 但是对于调试多多少少还是有帮助的:
devtool: 'source-map',
{
test: /\.js$/,
loader: 'source-map-loader',
options: { enforce: 'pre' }
- 编译到 Node.js 代码
大体上跟浏览器的配置很像, 但是提供了两个 :target
配置 :node-library
和 :node-script
.
:node-script
跟 npm-module
的用法很像, 我没用过, 估计是对 Node.js API 做了处理,
跟浏览器配置差不多, 也支持热替换, 可以看我写的例子:
关于编译的细节在 Wiki 上也有一些描述, 自己看啦, 估计作者文档没写完…
:advanced
模式
这个是 Closure Compiler 提供的编译选项, 用于代码优化.
以往的 ClojureScript 编译器也是支持的, 只是在这里针对 CommonJS 特殊处理了一下.
技术细节可以阅读 https://github.com/thheller/shadow-cljs/issues/24
ClojureScript 代码编译结果比较大, 需要剔除无用的代码, 要开启 :advanced
模式
但是这是需要写 externs 文件指明如何保留不删除和混淆的代码的…
对于大多数人来说编写 externs 是一件很头疼的事情, 所以还是要想点办法,
目前作者也讲还在考虑当中, 看如何才能把这个过程简化一下.
对比
大致使用体验的话, 可能启动会快一点, 毕竟不像 Boot 那么多初始化的东西,
不过 shadow-cljs 不提供 compile warning 在页面弹提示, 可能会不习惯,
现在应该遇到编译出错会在 Console 里打印的. 其他开发体验应该还好.
我是说直接用 :browser
模式开发, 工作流程其实差不多,
用了 Webpack 的话, 相当于是经过两个不同的工具编译, 不那么可靠,
但是引入 Webpack 主要还是为了 npm 模块还有打包之类的事情的方便吧.
Node 这边体验好一点, 以前的 lein-figwheel 虽然功能强大, 但是配置太烦了,
shadow-cljs 是开箱即用, :devtools
直接配好热替换, 直接有 REPL,
另外 Lumo 虽然也能玩 Node, 我的感觉是启动太慢了,
shadow-cljs 这边 watching compiling 开起来, 然后就是 node 启动的速度了.
而且 Lumo 的做法, 热替换需要自己处理, --inspect
选项不能用, 不够好.
其他
总体感觉是开启了很多的可能性, 特别是对 js 开发者能友好很多.
之前配置 classpath
真是搞得烦死了, 现在完全不用管.
最近 cljs 社区似乎在接入 npm 方面做了一些努力, 感觉能舒心一点.
现在看看开发当中, 还有往 Clojars 上传模块这个过程离不开 Boot 或者 Lein,
不知道近期有没有新的方案出来, 那样的话我手头的 build.boot
就能删了.
有点盼头总是好的…