转载

Respo 单页面应用 Demo 代码解释

Respo 确实是个轮子, 甚至不像是 react-lite 那样能替代 React

Respo 主要的目标是用 ClojureScript 重新实现一遍, 以及改进和学习

为了方便使用, 我把相应代码整理出一个模块, 方便的有兴趣的同学使用

https://github.com/mvc-works/respo-spa

随后我增加了一个 example 用来展示具体的使用方法

https://github.com/mvc-works/respo-spa-example

我用一个视频录制了从创建项目到完成界面的过程

http://www.tudou.com/programs/view/njte4UfduKw/

了解 cljs 的同学可以直接看 repo 里的内容, 代码实际上挺短了

具体的 API 在 resporespo-client 两个 repo

respo-spa 当中主要是封装出来比较友好的接口方便上手

这篇文章相当于一个文字版, 我介绍一下 respo-spa 的用法

首先看一下 Respo 用法上和 React 一些比较明显的区别:

  • states 是全局的, 不是 React 那样私有状态, 这有利于热替换

  • Respo 当中 element 的 DSL 分成 style event attrs 三类

    写起来有点长. 但是对于框架有好处, 需要的话自己可以再封装

  • Respo 中 state 和 Store 一样, 可以自己设置类型, 以及如何更新

  • Respo 中 mutate 是通过函数式的写法模拟 setState , 有区别

...此外 Respo 中缺少很多 React 中有的生命周期方法, 少了很多功能

毕竟不是文档, 我从源码开始分解吧, example 涉及了大部分功能了:

button.cljs :

(ns spa-example.component.button   (:require     [respo.alias :refer [create-comp div span]]     [hsl.core :refer [hsl]]))  ; 定义无状态的组件, ; 第一个函数参数对应 props (defn render [text on-click]   ; 第二个函数是 state 和修改 state 的函数   (fn [state mutate]     ; 注意这里, style event 是分开的, 所以是两层的 map     (div {:style {:background-color (hsl 200 80 70)                   :display "inline-block"                   :padding "0 8px"                   :color "white"                   :margin "0 8px"                   :cursor "pointer"}           :event {:click on-click}}       ; 属性是 inner-text, 内部翻译为 innerText       (span {:attrs {:inner-text text}}))))  ; 创建组件的写法, 没有 state, 因而参数较少 (def comp-button (create-comp :button render))

box.cljs :

(ns spa-example.component.box   (:require     [respo.alias :refer [create-comp div span]]     [spa-example.component.button :refer [comp-button]]))  ; 定义状态, 这里直接用来数字 (defn init-state [] 0)  ; 状态每次操作, 直接加上一个数字 (defn update-state [state step] (+ step state))  ; 处理事件, 其中 dispatch 由框架传递, mutate 由参数传递 (defn handle-click [mutate step]   (fn [simple-event dispatch]     ; mutate 会调用到 update-state, 第一个参数自动加上     (mutate step)))  ; 可以自己封装函数用来简化纯文本的 span (defn text [x]   (span {:attrs {:inner-text (str x)}}))  (defn render [n]   (fn [state mutate]     (div {}       (text (str n ". "))       ; 调用组件的语法, 就和函数一样用       (comp-button "inc" (handle-click mutate n))       (text state))))  ; 创建带状态的组件, 注意参数数量增加了 (def comp-box (create-comp :box init-state update-state render))

container.cljs :

(ns spa-example.component.container   (:require     [respo.alias :refer [create-comp create-element div span]]     [spa-example.component.button :refer [comp-button]]     [spa-example.component.box :refer [comp-box]]))  ; Respo 内部没有定义足够多元素, 可以自己绑定 (defn hr [props & children]   (create-element :hr props children))  (defn handle-click [simple-event dispatch]   (dispatch nil nil))  (defn render [store]   (fn [state mutate]     (div {}       ; 列表类型的元素渲染, 外边包裹 div, 里边用 sorted-map       (div {}         (->> (range 10)           (map-indexed (fn [index n]             [index (comp-box n)]))           (into (sorted-map))))       (hr {})       (comp-button "inc" handle-click)       (span {:attrs {:inner-text (str store)}})       (div {}         (comp-box)))))  (def comp-container (create-comp :container render))

core.cljs :

(ns spa-example.core   (:require [respo-spa.core :refer [render]]             [spa-example.updater.core :refer [updater]]             [spa-example.component.container :refer [comp-container]]))  ; 定义全局的 store 的引用 (defonce store-ref (atom 0)) ; 定义全局的 states 的应用 (defonce states-ref (atom {}))  ; dispatch 用来操作 store, 用的函数是 reset! 是有副作用的 (defn dispatch [op op-data]   (reset! store-ref (updater @store-ref op op-data)))  (defn render-app []   (let [target (.querySelector js/document "#app")]     ; render 函数来自 respo-spa 的封装, 强制传递一些参数过去     (render (comp-container @store-ref) target dispatch states-ref)))  (defn -main []   (enable-console-print!)   (render-app)   ; 在 store 和 states 发生改变是重新绘制界面   (add-watch store-ref :changes render-app)   (add-watch states-ref :changes render-app)   (println "app started!"))  (defn on-jsload []   ; 在 js 代码热替换之后重新绘制界面   (render-app)   (println "code updated."))  (set! (.-onload js/window) -main)

updater/core.cljs 只是说明一下可以定制:

(ns spa-example.updater.core)  (defn updater [store op op-data]   (inc store))

整体上都还是我之前在 React.js 当中用的那些套路, 只是更纯粹一些

代码的稳定性还有待时间, 毕竟使用的项目太少, 完全不能保障

生命周期方法的缺失大概会影响具体的使用, 只是程度现在不确定

同时 mutate 函数实际上印象性能, 还有待优化, 我现在并没有处理

视频部分非常详细了, 有兴趣可以照着试试看玩 cljs, 有问题微博上问我

原文  https://segmentfault.com/a/1190000005061680
正文到此结束
Loading...