Clojure China

关于 Lumo 的代码热替换

#1

录个视频解释一下 http://weibo.com/1651843872/EqdyOFWnY?ref=home&rid=1_0_1_2815985669887175627

因为在 Webpack 里热替换用惯了, 所以到 ClojureScript 环境也少不了想要,
本来折腾半天用了 Figwheel 的, 经过几次升级发现配置变动压力很大,
原因是我用的 Boot, 需要额外的插件来处理, 升级经常遇到配置出错的问题, 而且配置真的很长:


所以在 Lumo 发布之后我就在尝试切换到 Lumo, 并且尝试用 Lumo 的热替换.

Lumo 里大致上就是跟 Clojure 差不多的 require 函数的功能, 可以替换模块:

(require 'a.b :reload)

一般我会开启 Lumo 的缓存, 因为编译太慢了, 结果连缓存文件也需要手动清除,
挺麻烦的, 一直就弄成教脚本, 也跟官方提过 Issue, 但官方没这个心思做,
昨天继续尝试了一下, 得到了微信群里同学的帮忙, 扒了一些 Lumo 的源码出来,
最终得到一份关键代码, 可以配合文件更新事件来替换命名空间:

(defn node-eval [{:keys [name source]}]
  (.runInThisContext vm source (str (munge name) ".js")))

(defn eval-inside! [st code]
  (cljs/eval st code
    {:eval node-eval :load @#'lumo.repl/load}
    (fn [error]
      (println "Error:" error))))

(defn handle-reload! [ns-path]
  (let [st (cljs/empty-state)
        segment (-> ns-path
                    (string/replace "-" "_")
                    (string/replace "." "_SLASH_"))]
    (println "Removing:" segment)
    (cp.execSync (str "rm -rf .lumo_cache/" segment "*"))
    (eval-inside! st `(~'require (quote ~(symbol ns-path)) :reload))
    (main/on-jsload!)))

有兴趣玩的话, 完整代码在这边:

https://github.com/Cumulo/cumulo-workflow/tree/master/server

关键两个难点, 一个是 require 参数需要是 symbol, 这个宏挺难写的, 抄过来的.
另一个是 eval, 它并不是官方开放出来的 API, 不看源码我都不知道有这玩意,
虽然以后我尝试翻过 Lumo 的源码来着, 也是知难而退了.

能热替换的话… 架构设计得好的话也能跟前端一样玩了.

#2

对于 Clojure 来说, 更合适的做法应该是开个 repl, 连接上 repl,更改过后,直接 re-eval 部分代码或者整个 buffer。
这就是 cider 目前的做法。

#3

函数级别直接替换么? 不知道怎么实现呃… 要是能做出来就好了, 我自己抄一个的话