clojurescript 零零碎碎

控制台查看db信息

在浏览器中开启自定义日志后可以在console中查看re-frame管理的db里的内容,加格式化。
开启方式:https://github.com/binaryage/cljs-devtools/blob/master/docs/faq.md#why-some-custom-formatters-were-not-rendered

image.png

image.png

在console里使用如下命令查看

re_frame.db.app_db.state

效果:


re_frame.db.app_db.state

用kee-frame和re-frame修改db的值

目标给db的【demo】下增加一个key,值是一个ajax的返回结果
引入两个包

(require '[kee-frame.core :as k])
(require '[re-frame.core :as re-frame])
  • 使用re-frame/reg-event-db修改db
(defn- concat-key [keys]
  (concat [:demo] keys))

(defn- data->db [db keys value]
  (assoc-in db (concat-key keys) value))

(k/reg-event-fx
 ::demo-detail-ajax
 (fn [{:keys [db]} [id]]
   {:http-xhrio {:uri ("http://localhost:3000/api/demo?id=" id)
                 :method :get
                 :timeout 10000
                 :on-failure [:common/ajax-get-failuer]
                 :response-format (ajax/json-response-format {:keywords? true})
                 :on-success [::set-db-demo-detail]}}))

(re-frame/reg-event-db
  ::set-db-demo-detail
  (fn [db [_ res]]
    (data->db db [:demo-detail] (:data res))))
  • 上面的操作用kee-frame实现
(k/reg-chain-named
 ::demo-detail-ajax
 (fn [ctx [param]]
   {:http-xhrio {:uri ("http://localhost:3000/api/demo?id=" (:id param)
                 :method :get
                 :timeout 10000
                 :on-failure [:common/ajax-get-failuer]
                 :response-format (ajax/json-response-format {:keywords? true})}})

 :将返回结果数据存入db
 (fn [{:keys [db]} [_ res]]
   {:db (data->db db [:demo-detail] (:data res))}))

区别:

  • re-frame里发起请求和存储db需要两个事件来实现,而kee-frame需要一个,第二个还可以起中文的名字,便于在控制台查看。
  • re-frame/reg-event-db返回的是(assoc db :key value),而kee-frame/reg-chain返回的是{:db (assoc db :key value}
  • kee-frame的这种返回类似于re-frame/reg-event-fx的另一种形式
(defn h                               ;; maybe choose a better name like `delete-item`
 [coeffects event]                    ;; `coeffects` holds the current state of the world.  
 (let [item-id (second event)         ;; extract id from event vector
       db      (:db coeffects)]       ;; extract the current application state
   {:db  (dissoc-in db [:items item-id])})) ;; effect is "change app state to ..."

(re-frame.core/reg-event-fx   ;; a part of the re-frame API
  :delete-item                ;; the kind of event
  h)  

这个处理github上是有的,这个h函数换可以简化一下

(defn h 
  [{:keys [db]} [_ item-id]]    ;; <--- new: obtain db and id directly
  {:db  (dissoc-in db [:items item-id])}) ;; same as before

antd的组件中使用组件

一些组件需要的属性的参数类型是ReactNode, 比如Input的prefix属性
这需要 使用Reagent的as-element函数

(as-element form) Turns a vector of Hiccup syntax into a React element. Returns form unchanged if it is not a vector.

[:> ant/Input
          {:prefix (r/as-element [:> ant/Icon {:type "user"}])}]

修改页面atom元素赋值和修改

初学者包括小菜鸡我是不是会碰到个问题,从db里订阅的数据渲染页面时第一次没有,第二次以后就都正常了,或者你想修改一下这个值重新提交到接口上,发现值改不了。

可能你的代码是这样的

(defn page []
  (let [ data @(re-frame/subscribe [:db-key])]
    (fn []
      [:div 
          [ant/input {:default-value (:name @data)
                          :type "text"
                          :on-change #(swap! item assoc item-str (-> % .-target .-value))}]])))

亦或是这样的:

(defn page []
  (fn []
    (let data @(re-frame/subscribe [:db-key])
         [:div [ant/input {:default-value (:name @data)
                          :type "text"
                          :on-change #(swap! item assoc item-str (-> % .-target .-value))}]])))

恭喜,这两种方式完美踩雷,我们大神说因为我没有看reagent
的Guide一二三,好吧,确实没看。

  • 现状和原因

  • 第一种写法的现状:页面的input里第一次无法赋值

  • 第一种写法的原因:页面加载一次,但是第一次订阅不到db里的数据,因为db里还没有数据

  • 第二种写法的现状:页面input上有值了,但是不能修改,提示react的value不能被修改

  • 第二种写法的原因:从db里订阅的值data订阅之后就与订阅没有关系了,说白了,也就不是atom了,所以不能修改。

  • 方案:

(def db-value
  (reaction @(re-frame/subscribe [:db-key])))
(def change-value (atom nil))

(defn page []
  (fn []
    (reset! change-value @(if (nil? @db-value)
                            (atom (deref (rf/subscribe [:db-key])))
                            db-value))
    [:div
     [ant/input {:default-value (:name @db-value)
                 :type "text"
                 :on-change #(swap! item assoc item-str (-> % .-target .-value))}]]))

当然,如你所知,写法有很多种,比如这个change-value你可以在fn里用let定义,并且比较推荐。

kee-frame/reg-controller 有没有好好看文档?

(defn reg-controller
  "Put a controller config map into the global controller registry.

  Parameters:

  `id`: Must be unique in controllere registry. Will appear in logs.

  `controller`: A map with the following keys:
  - `:params`: A function that receives the route data and returns the part that should be sent to the `start` function. A nil
  return means that the controller should not run for this route.

  - `:start`: A function or an event vector. Called when `params` returns a non-nil value different from the previous
  invocation. The function receives whatever non-nil value that was returned from `params`,
  and returns a re-frame event vector. If the function does nothing but returning the vector, the surrounding function
  can be omitted.

  - `:stop`: Optional. A function or an event vector. Called when previous invocation of `params` returned non-nil and the
  current invocation returned nil. If the function does nothing but returning the vector, the surrounding function
  can be omitted."
  [id controller]
  (when-not (s/valid? ::spec/controller controller)
    (e/expound ::spec/controller controller)
    (throw (ex-info "Invalid controller" (s/explain-data ::spec/controller controller))))
  (when (get @state/controllers id)
    (console :warn "Overwriting controller with id " id))
  (swap! state/controllers update id merge controller))
  • kee-frame引入controller来优化路由路由管理,监听路由变化,所以在路由发生变化时程序里的所有controler都会竖起耳朵,当然最好是只有这个变化和自己有关,才去响应。这就要在params里加handler的判断。
  • :params 参数是一个函数包含路由信息,尤其是获取handler里的路由地址,当然包含参数。如果返回nil的话,后面start就不会执行,其他情况就会触发:start
  • :start 一个函数或者一组事件的集合,函数里可以处理参数等,如果是一堆event的话,可以在函数里发起dispatch或者直接返回[:event1 ::event2]等等。
    来个基础的例子:
(k/reg-controller :bind-user-page
                  {:params (fn [params]
                             (when (= :bind-user-page (get-in params [:data :name])) true))
                   :start (fn []
                            (prn "?????????===")
                            (re-frame/dispatch [:choose-hospital]))})

携带query参数的例子:

;;路由带参跳转不同页面
(k/reg-controller
 :路由带参跳转不同页面
 {:params (fn [route-data]
            (when (-> route-data :data :name (= :weixin-redirect))
              (:query-string route-data)))
  :start (fn [_ params]
           (re-frame/dispatch [:weixin-redirect
                               (reduce-kv (fn [m k v]
                                            (assoc m (keyword k) v))
                                          {}
                                          (into {} (map #(clojure.string/split % #"=") (clojure.string/split params #"&"))))]))})

如果想要每次路由变化,都触发某个controller的start,只需要在params里返回identity即可。

clojurescript 和 javascript交互

有时候我们的clojurescript需要和javascript交互。
比如用js打个log,比如用js获取window的location数据等,举个列子

(.log js/console "打个log")
(.-location js/window)
(.-search (.-location js/window))

cljs还提供了cljs和js互转的函数clj->jsjs->clj
看看例子:

(def js-object (clj->js  :a 1 :b [1 2 3] :c #{"d" true :e nil}))

输出结果

{
  "a": 1,
  "b": [1, 2, 3],
  "c": [null, "d", "e", true]
}

也可以简化用#js

(def js-object #js {:a 1 :b #js [1 2 3] :c #js ["d" true :e nil]})
# 输出
{
  "c": {
    "e": null,
    "d": true
  },
  "b": [1, 2, 3 ],
  "a": 1
}

js有时候需要转成cljs,用js->clj实现,比如:

(defn get-form
  "返回又From.create创建的 `form` 这个函数只能在form内部调用, 因为使用了reaget/current-component."
  []
  (-> (r/current-component)
      (r/props)
      (js->clj :keywordize-keys true)
      (:form)))

大神在4clojure博客上也有js操作DOM和引入highcharts的使用的例子。
更多交互的操作参考ClojureScript: JavaScript Interop

用figwheel编译工程,启动服务

三步启动 figwheel:Using the Figwheel REPL within nREPL
在emacs里启动cider-jack-in-cljs
选择figwheel

(use 'figwheel-sidecar.repl-api)
(start-figwheel!)
(cljs-repl)

在alk项目里的示例:
在emace里按照如下三不启动:

  1. cider-jack-in-cljs
  2. lein
  3. figwheel
    启动后到user这个namespace
user> (use 'figwheel-sidecar.repl-api)
nil
user> (start-figwheel!)
Figwheel: Starting server at http://0.0.0.0:3449
Figwheel: Watching build - patient-app
Compiling build :patient-app to "target/cljsbuild/public/patientjs/app.js" from ["src/cljs/patient" "src/cljc" "env/dev/cljs/patient"]...
Successfully compiled build :patient-app to "target/cljsbuild/public/patientjs/app.js" in 5.142 seconds.
Figwheel: Starting CSS Watcher for paths  ["resources/public/css"]
Figwheel: Starting nREPL server on port: 7002
nil
user> (cljs-repl)
Launching ClojureScript REPL for build: patient-app
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild id ...)        ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once id ...)             ;; builds source one time
          (clean-builds id ..)            ;; deletes compiled cljs target files
          (print-config id ...)           ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
          (figwheel.client/set-autoload false)    ;; will turn autoloading off
          (figwheel.client/set-repl-pprint false) ;; will turn pretty printing off
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application
To quit, type: :cljs/quit
nil
 cljs.user> (fig-status) 
cljs.user> (fig-status) 
Figwheel System Status
----------------------------------------------------
Watching builds: [patient-app]
Client Connections
     patient-app: 1 connection
----------------------------------------------------
nil
cljs.user> (build-once doctor-app)
Figwheel: Building once - doctor-app
Compiling build :doctor-app to "target/cljsbuild/public/doctorjs/app.js" from ["src/cljs/doctor" "src/cljc" "env/dev/cljs/doctor"]...
Successfully compiled build :doctor-app to "target/cljsbuild/public/doctorjs/app.js" in 14.038 seconds.
nil
cljs.user> (fig-status) 
Figwheel System Status
----------------------------------------------------
Watching builds: [patient-app]
Client Connections
     patient-app: 0 connections
     doctor-app: 1 connection
----------------------------------------------------
nil
cljs.user> 

用浏览器链接后可以在repl里查看状态

(fig-status)  

还有其他操作,控制台有提示

user> (cljs-repl)
Launching ClojureScript REPL for build: dev
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (print-config [id ...])         ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application
To quit, type: :cljs/quit
cljs.user>

你可能感兴趣的:(clojurescript 零零碎碎)