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

shadow-cljs.edn
拆分出1个shared 
 :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。显得有点乱

 

 

 

 

posted @ 2022-03-07 14:41  永远的幻想  阅读(180)  评论(0编辑  收藏  举报