补了一些关于 Respo Effects 的文档, 英文细节有点吃力,
https://github.com/Respo/respo/wiki/defeffect
关于 Respo 的设计思路和功能取舍, 这边能够再描述详细一些.git
比方说有个组件要增长反作用,github
(defcomp comp-a [x y z] (div {}))
此次更新之后, respo.core
当中新增了一个 defeffect
的宏用来定义反作用,defeffect
须要的不仅仅是多个参数, 并且是不少组参数.数组
(defeffect effect-a [x y] [action el *local] (println "effects"))
[x y]
固然就是参数了. 框架渲染过程中会自动插入参数的值,
另外框架会插入 action
表示 :mount
:update
:unmount
,
以及 el
是组件根节点, 也是由框架获取.框架
这个宏的实现, 就是把代码转换成一个函数, 函数返回的是个 HashMap,函数
(defmacro defeffect [effect-name args params & body] `(defn ~effect-name [~@args] (merge respo.schema/effect {:name ~(keyword effect-name) :args [~@args] :coord [] :method (fn [[~@args] [~@params]] ~@body)})))
上面定义获得的 effect-a
就是一个函数, 能够经过 (effect-a x y)
调用,
在组件当中使用的时候, 就是把返回值变成数组, 在数组当中加上反作用spa
(defcomp comp-a [x y z] [ (effect-a x y) (div {}) ])
后面就依靠 Respo 的渲染代码, 内部进行判断, 对 effect 进行处理.设计
因为 effect 没有直接区分开不一样的生命周期, action
使用时须要自行判断,code
(case action :mount (do) :update (do) :unmount (do) (do))
*local
的存在, 是为了应付可能存在的存储局部状态的需求.
好比在 mount 的时候建立的数据, 若是在 update 和 unmount 须要用到,
目前的设计当中, 就须要组件提供私有的状态用于传递.
须要注意, 这个 *local
实际上对应的 React 当中的 ref,
也就是说, 在 *local
上修改数据, 不会出发 rendering 的行为.component
React 当中组件定义的方式比较简单,生命周期
(defcomp comp-a [x y] (div {} (div {} (<> "DEMO"))))
而后会通过一次宏展开, 宏的实现是
(defmacro defcomp [comp-name params & body] `(defn ~comp-name [~@params] (merge respo.schema/component {:args (list ~@params) , :name ~(keyword comp-name), :render (fn [~@params] (defn ~(symbol (str "call-" comp-name)) [~'%cursor] ~@body))})))
上面的组件通过 (comp-a x y)
这样的调用以后, 会获得一个 HashMap,
{:name :comp-a :args '(x y) :render (fn [x y] (defn call-comp-a [%cursor] (div {} (div {} (<> "DEMO")))))}
能够看到其中没有实现生命周期的信息.
这个高阶函数在运行时会继续被处理, 添加所需的参数, 再被计算.
这个结构当中并无预留跟 React 类似的组件生命周期,
并且也不适合用方法进行扩展, 因此比较难直接有 React class 组件那种写法.
若是须要在组件当中支持反作用的, 至少要在组件的表上加上 effects 的位置,
{:name :comp-a :args '() :render (fn []) ; add :effects [(fn [])]}
原先的 defeffect
的 API 写出来, 我大体肯定了须要哪些参数,
好比 [a b]
参数, 界面渲染和更新当中使用,
而后是 action
用来判断生命周期, el
对应 React 当中的 DOM Ref 用.
有了 defeffect
以后我在考虑, 都是把 effect 插入在 DOM 树当中,
相似 (div {} (effect-a x y) (div {}))
这样,
但具体看了实现, 涉及到 DOM Diff 的实现有不少坑, 也就做罢了.
因而想怎样才能以兼容已有的写法的方式吧反作用插入进去.. 最简单就是数组了.
用数组的话, 能够插入多个 effect, 而且后续也有些许继续扩展的能力.
这套写法跟 React Hooks 比起来, 有很多的功能缺失.
特别是 Respo 当中, 基本没有运行渲染过程再 dispatch actions 的可能.
React 当中频繁有 componentDidMount 或者 useEffect, 在任什么时候候修改组件状态,
并且也没有限制在这种生命周期时 dispatch actions.
Respo 里不承认这样的作法, 这样会持续衍生出 actions 来.
特别是在 Time Traveling 的场景当中, 这种 actions 就是破坏性的,
一旦切换到旧的某个 action 致使新的 actions 被触发, 状态就未必一致了.
总体考虑为了热替换方便, 组件局部状态的变化, 是不鼓励的.
目前 Respo Effects 算是出如今早期状态, 后面也可能再调整.
无论怎样, 此前 Respo 为了实现纯的渲染, 没有作 effects,
致使跟 JavaScript 生态已有的一些用法不能轻松衔接.
如今加上了 Effects, 那些东西终于能够进行尝试了.
Respo 最第一版本是 2016 年初开始的, 年中基本完成,这么多年了, 用的人少, 这方面的需求也没有太大的问题, 由于场景也有限.我我的以为 Effects 不会有太多的须要. 仍是以小范围扩展功能位置.