Replies: 2 comments
-
前几天对事件处理部分用到的 coord 进行了重构, 性能提升明显 https://github.com/Respo/respo/pull/35/files . 事件处理的过程也算梳理了一下. 大致还是上边的叙述的, 目前代码当中用到的两个 coord 变量,
现在组件树展开的过程, coord 是不参与的, 还是那个例子, 同样一个组件可能用在多个位置, coord 会不同, 就这个优化而言, 也就回到了函数式本身的性能开销上边来了, 倒不是说函数式编程一定会慢. 应该说, 这就抽象和简洁所随之而来的代价. |
Beta Was this translation helpful? Give feedback.
-
为什么要函数式编程, 而不是通用的 JavaScript 的写法? 我前几天本来想找找 preact 做一下组件状态对比的, 但是翻开代码, 发现内容好多. 但是这样一比较下来, Respo 的代码真的算是很简单明了了, expand, diff, event resolve, patch DOM, 步骤分得很明确.
对于 Vue 和 Svelte 的态度, 我从 React 这边能感受到只用函数的局限性, 不过 Vue Svelte 编译方案, 也会有编译器实现的难点, 另外一个方向是对于 PureScript Elm 方案的态度, 只是说我碍于代码量的问题, 到目前为止还不情愿从 cljs 这个舒适区离开, 最后还是绕回来说为什么不选 js 的事情,
再往大了说, 我上面说想要抽象甚至 codegen 的能力, 基于 js 那还得再上 Babel 才能玩起来啊, 栈很长. 唉, 至于说真实项目的实用性和普适性, 想想 preact 源码当中那些细节, 真是太大的话题了. |
Beta Was this translation helpful? Give feedback.
-
早期版本以 Deku 作为参考, 实现根据 ClojureScript 的不可变数据方式来做.
中间也经历过演化, 特别是事件处理逻辑比较绕. 算法部分 diff 算法研究不够深入.
global states 跟 effects 是后续加入的, 比较偏执.
相对于 React 来说, 局部状态. 组件内部的数据逻辑和生命周期, 实在太弱了.
Respo 和 Deku 的对比
Diff 算法
向前查看 N 个节点(似乎是 16)判断 children 是否增删, 性能不够.
事件处理机制
全局的 component tree 上边有 coord 保存节点位置信息.
点击事件当中有 coord 的信息, 这时候需要反过来到 component tree 上查找事件.
这个地方特别的是, 组件当中描述的事件是通用的, 只是用来标记事件是否存在以及 coord 信息.
所以点击时候, 是需要在 component tree 当中去找到真实的事件再进行调用的.
这样对 diff 那边有一些好处, 因为 diff 的时候只管事件是否存在, listener 的闭包有改变, 是不产生 diff 的.
这样优化的时候缓存组件, 也就要根据这个事情进行考虑.
比较麻烦的是, coord 是需要比较晚才能确定的, 不能缓存,
或者说组件调用在不同位置代码是一样的, coord 是在渲染才能确定的, 无法缓存.
组件树渲染过程的复用
由于 React 中 state 改变, 组件计算是从局部的组件开始的, 能对应到上次渲染的结果,
这样是比较容易进行判断的, 就跟当前 class instance 直接判断就好了.
Respo 做了分离, 因而就需要手动控制, 跟旧的树进行对比.
当时做的方案是存一棵树, 新的树计算过程就以已有的树对应的分支作为参数进行计算,
这样的好处是, 如果发现参数一致, 就可以直接复用已有的这棵树了. 节省 diff 的时间.
注意, 虽然 React 当中用 shallow equal 的方式, 甚至引用相等的方式一致性,
实际在 cljs 当中, 数据要判断, 还是需要递归判断整个结构的.
引用一致只是特殊情况. 而且判断具体跟 persistent data 的实现有关系.
现在 calcit-js 版本的 Respo 用的是 ternary-tree, 那么这个性能的因素也发生改变了.
Memof 方案
前面说的计算过程当中使用上次计算得到的树的编码方式, 对实现逻辑来说侵入性比较大.
我后来考虑如果找更自然的方法, 就是直接用 memoization 做.
而真实场景, memoization 保存的信息是需要借鉴 GC 的方式进行清理的, 否则占用大量内存.
从 React 的角度来说, 只有上次计算存在而且这次继续计算依然存在, 才会保留对应信息.
目前 memof 的设计思路, 只是简单判断了一下, 循环多次, 数据没有被触碰到超过一定次数, 就丢弃.
这个策略还是比较粗略的, 以后还是想想什么办法可以监测定量一下.
之前版本 memof 使用的时候, 是用 macro 配合插入访问缓存的代码.
现在的版本
memof-call
通过高阶函数手动调用, 更加灵活.Beta Was this translation helpful? Give feedback.
All reactions