Clojure China

开发环境 ClojureScript 服务端代码热替换

#1

Node.js

其实就是 Node.js 代码开发环境热替换, 只是多了 cljs 的编译过程.
直接用 JavaScript 社区的办法, 主要是两种:

  • 通过脚本清除 require.caches 里的缓存, 重新载入代码,
    并不是十分安全的办法, 只是说开发环境够用而已.

  • 通过 Webpack 的热替换功能, 加上 target: 'nodejs' 设置,
    和浏览器端的热替换差不多, 实际情况也还行.

Figwheel

不过对于 Node.js 开发来说并不常用, 还是喜欢重启整个服务器…
cljs 这边情况差不多, 毕竟编译到 Node.js 运行的.
之前有 Figwheel 提供了热替换编译后代码的功能, 顺带支持了 Node 环境,
不过 Figwheel 的配置并不轻松, 特别是用在 Boot 脚本里边.
主要是启动缓慢, 编译脚本的时间格外长, 以及配置复杂, 版本更新问题,
因此我一直在找 Boot 环境的替代工具, 只是并没找到.

其实 Clojure 当中是对热替换做了一些支持的,
之前也写过, 主要就是 :reload 参数的用法, 以及一些变种:


https://gist.github.com/ghoseb/287710/
也就是说 Clojure 编译过程就考虑了运行时替换代码的情况.
并不需要利用 require.caches 之类的 API 处理, 编译器本身处理了.
而 cljs 的 REPL Planck 早先也支持了这个功能.

Lumo

Lumo 是上个月发布的一个 REPL, 基于 V8 实现, 同时支持了 npm 模块,


之前 Planck 基于 JavaScriptCore 实现, 因此不支持引用 npm 模块,
这样我通过 npm 模块启动 WebSocket 实际上不能用 Planck 运行,
那么 Planck 就不能用来执行代码了, 我只能用 Figwheel 编译掉,
而 Lumo 就可以了, 于是原来 (require 'x :reload) 的写法就能用了.

那么我定义几个文件:

=>> tree
.
└── hot
    ├── lib.cljs
    └── main.cljs

1 directory, 2 files
=>> cat hot/lib.cljs

(ns hot.lib)

(def a 444)
=>> cat hot/main.cljs

(ns hot.main
  (:require [hot.lib :as lib]))

(defn echo! []
  (println lib/a))

然后选取相应的路径启动 Lumo,
其中 -c 设置命名空间, -i 设置脚本, -r 启动 REPL:

=>> lumo -c ./ -i hot/main.cljs -r
Lumo 1.0.0
ClojureScript 1.9.293
 Docs: (doc function-name-here)
 Exit: Control+D or :cljs/quit or exit

cljs.user=>

然后可以通过脚本启动函数打印变量:

cljs.user=> (require '[hot.main :as main])
nil
cljs.user=> (main/echo!)
444
nil

接着我修改 hot/lib.cljs 当中 a 的数值,
在不退出 REPL 的情况下, 我也可以通过 :reload 重新加载代码,
然后打印出来的变量就变了:

cljs.user=> (require 'hot.lib :reload)
nil
cljs.user=> (main/echo!)
555
nil
cljs.user=>

原理就是这个原理, 对于深度嵌套的文件也是适用的, 验证可以直接替换掉数据,
另外要注意的是, 在 Node 里一个更新是需要冒泡的, 这样依赖它的代码也更新掉,
而 cljs 当中基本采用函数式的写法(尽管还是遗留 IO 等副作用), 依赖比较少,
所以替换掉数据就可以了, 不需要冒泡.
通过在顶层会继续用 main/on-jsload! 这样一个函数, 在热替换后刷新一下程序…

闲着没事看下我微博录的视频: http://weibo.com/1651843872/ElpKt0YvI?ref=home&rid=9_0_1_2676182869389349214

1赞
#2

我记得 ClojureScript 1.9 可以用 :refer 代替 :refer-macros, 这个不行

#3

我在 boot 环境也试了一下, :refer-macros 不成功, 也蛮奇怪的. 1.9.

#4

目前好像没有 emacs 支持 lumo, 要是有的话, 开发 cljs 就太方便了

#5

而且还不支持 cljc 文件

#6

不晓得, 但是我的类库里有用到 cljc, 并没有报错嘛/

#7