美文网首页
clojurescript 零零碎碎

clojurescript 零零碎碎

作者: 小马将过河 | 来源:发表于2019-08-26 13:31 被阅读0次

    控制台查看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 零零碎碎

          本文链接:https://www.haomeiwen.com/subject/prjpcctx.html