shadow-cljs clojurescript Cookbook
虽然有官网api文档, 虽然有https://cljs.info/cheatsheet/, 虽然有clojure的书和clojure开发经验.但总还是有一些磕磕绊绊.
自己写个cookbook,备查.
1. REPL报错 already started
npx shadow-cljs stop
calva命令里缺乏停止/重启 REPL
只能自己手动停止, 然后再用calva启动一个就OK
2 .用字符串函数名,调用 js定义的window.XXX 函数
应该叫反射吧,不懂.反正搜索半天,都不work
最后这样
(defn get-fn [fn-name]
(js/eval (.format "window.%s" fn-name)))
简单说,利用 cljs的js/eval, 用js的eval函数,对"window.XXX"求值,就得到了函数, 然后就可以调用了. 不是利用cljs的机制,而是利用寄生平台js 引擎的机制. 毕竟js也是参考lisp,有eval,比较functional的嘛.
3. shadow-cljs 依赖本地js文件
比如 XXX.min.js 或者自己写的单个js文件.
希望shadow-cljs把这些和自己的cljs一起作为src打包成1个bundle
文件这样放:
src/js 下 assets文件夹下 放别人开发好的js库, 自己写的config.js放点全局变量啥的
shadow-cljs.edn 这样写:
;; shadow-cljs configuration {:source-paths ["src/dev" "src/main" "src/test"] :dependencies [] :dev-http {8080 "public"} :builds {:frontend {:target :browser :modules {:main {:init-fn frontend.app/init} } :js-options {:resolve {"stomp" {:target :file :file "src/js/assets/stomp.umd.min.js"} "config" {:target :file :file "src/js/config.js"} } }}} }
:file的路径注意从工程根目录开始写起
app.cljs里这样写
(ns frontend.app (:require [frontend.util :refer (console-log)] [stomp] [config :refer [para_stomp]] ) )
4. 本地js文件依赖另一个js
js下假如有a.js b.js两个 需要在a中引用b.js的函数
shadow-cljs.edn
注意 b 一定不能和node_module其他模块重名,否则会被遮蔽
:js-options { :resolve {"a" {:target :file :file "src/js/a.js"} "b" {:target :file :file "src/js/b.js"} } }}}
a.js中
//import {func1} from './js/b.js' import {func1} from 'b' ....
注意不能写成 ./js/b.js 因为模块b 是shadow-cljs导入的。
等于自己写的本地b.js的导入方式,和安装的node_modules中的模块导入方式是一样的。
5. js object作为函数参数
图省事,直接吧参数放js里了, 最好还是用js去组装
也有clj->js函数可以吧clojure数据结构转成js object,但没试验.
6. 调用js 对象的方法 提示 warning infer XXX
Cannot infer target type in expression (. fnname obj)
尤其是 这个obj 的构造函数是 js里声明, obj 是(new )出来的
要声明这个obj的类型注解
比如new 一个js的对象,然后调用它的方法.
const client_stomp = new Client() client_stomp.activate()
cljs这样写:
(ns frontend.app (:require [frontend.util :refer (console-log)] [stomp] [config :refer [para_stomp]] ) ) (def ^stomp/Client client-stomp) ;增加类型注解 (defn init [] (console-log "Hello World") (console-log para_stomp) (set! client-stomp (new stomp/Client para_stomp)) (console-log client-stomp) (aset client-stomp "onConnect" (fn [frame] (console-log "stomp connected"))) ;; (console-log client-stomp) (.activate client-stomp ) )
声明时必须加上类型注解 ^stomp/Client
7. call cljs function from js
从js调用cljs写的函数。很麻烦,绕弯才搞定。
假如cljs模块
(ns frontend.maneuver (defn ^:export greet [] "Hello world!") )
7.1 切分导出多个modules(js文件)
参考shadow-cljs官方文档, https://shadow-cljs.github.io/docs/UsersGuide.html#target-browser
:builds {:frontend {:target :browser :compiler-options {:output-feature-set :es2020} :modules { :shared {:entries [frontend.maneuver]} :main {:init-fn frontend.app/init :depends-on #{:shared}} }
这样,编译后
public/js下出现2个js文件 shared.js和main.js
7.2 修改index.html
<html> <head> <link rel="stylesheet" type="text/css" href="/css/main.css"/> <meta charset="utf-8" /> </head> <body> .... <script src="/js/shared.js"></script> <script src="/js/main.js"></script> </body> </html>
7.3 js中调用文件
不用import。直接调用 frontend.maneuver.greet()
基本解决问题。只是没法import。显得有点乱