V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
qiuyk
V2EX  ›  JavaScript

React 的最小实践 - Kut

  •  2
     
  •   qiuyk · 2018-03-29 21:04:05 +08:00 · 2514 次点击
    这是一个创建于 2431 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Kut

    npm travis-ci

    Kut,一个简单的 React-Like 的前端视图渲染库,是我在学习 React 源码时造的轮子。Kut 是基于 Typescript 的 React 最小实现。目前 Kut 支持的 Top-Level 方法仅有两个,即 createElement、render,同时也支持组件化开发,Demo 在这里

    本文主要对 Kut 的源码进行说明和记录,项目地址:Github

    源码说明

    源码都在src目录下,目前一共 9 个文件,分别如下。

    component.ts

    定义了 Component 类,以用于自定义组件,只是定义了一些属性和方法,与 React 的 Component 类似。

    element.ts

    Element 是用于构造 Virtual DOM 节点的对象,element.ts 中包含一个工厂函数 createElement。Element 有 3 个属性:type 是类型,可以是 DOM tag (如 div 等)或自定义组件(即 Component 子类); key 用于 diff 时对节点进行唯一区分; children 是子节点数组,其元素可以是文本或者 Element,和 React 的区别是,如果 children 只有一项时,Kut 的 children 仍是数组,不过只有一项。

    instance.ts

    instance.ts 中包含了三种不同类型 Element 对应的实例类 Instance,对应 ReactComponent (注意区别 Component,为避免混淆,Kut 中命名为 Instance ),分别为文本实例 TextInstance、DOM 节点实例 DOMInstance 和自定义组件实例 ComponentInstance。三种 Instance 类结构基本类似,首先包含其对应于 DOM 节点的唯一 kutId,以方便进行挂载、更新和卸载;而 index 只用于列表项没指定 key 时使用,可忽略; key 和 node 用于获取 DOM 节点的 key 和节点本身。

    Instance 的价值主要在于 mount、shouldReceive、update 和 unmount 方法。mount 方法用于遍历 VDOM 树,拼接 HTML 和添加监听函数。而 shouldReceive 用于判断是否为同一节点,若 Element 的 type 和 key 相同,则直接调用 update 更新,否则调用 unmount 卸载并重新 mount 挂载。update 方法则递归更新以当前节点为根节点的 VDOM 子树,其中若 children 大于一项的,会使用 diff 算法计算其差异并调用 patch 进行更新。最后,unmount 方法则从 DOM 树上卸载节点,并清除引用。

    diff.ts

    对于列表项更新,需要使用 diff 算法计算其差异。React 的实现可以参考这篇文章,我称其为前向 diff。Kut 基本的实现逻辑和 React 是相似的,但对于把元素从列表中底部挪到顶部的做法,React 的前向 diff 会导致 DOM 更新操作过多。Kut 的做法是引入后向 diff,逻辑是和前向 diff 一致,只是方向相反,时间复杂度仍为 O(n)。取前向 diff 和后向 diff 的更新操作较少者,调用 patch 函数对 DOM 进行更新。这部分解释我都写在了 diff.ts 的注释里了。

    kut.ts renderer.ts constant.ts util.ts

    分别是入口文件、渲染方法、一些常量和一些工具函数。其中 renderer.ts 中定义了 Element 实例化 instantiate 函数(即由 Element 生成 Instance )和 render 函数(使用 innerHTML 进行挂载),由于采用 innerHTML 方法进行挂载,需要使用事件委托来处理事件,也需要使用 DOM 节点唯一 kutId 进行区别,具体见 event.ts 。

    event.ts

    React 为保证兼容性,具有合成事件。而 Kut 为简单起见,仍使用原生事件,采用事件委托的方式,将所有监听函数挂在 document 上。event.ts 的做法参考了的做法,实现了在 document 上添加和删除监听函数的方法,并以 kutId 判断触发的节点。

    后续计划

    最近忙着实习面试和论文暂时也没太多时间加新功能,现在仍然有些 bug,如 componentDidMount 的触发时机不对。慢慢先打算支持异步更新和 Context,起码让 Redux 能用对吧,先写篇记录免得后头来看连自己都忘了(苦笑)。推荐个最近看到的关于 React 源码的专栏,感觉讲得还不错的,在这里:编程小思

    欢迎 pr 和 stars,项目地址:Github

    第 1 条附言  ·  2018-04-07 23:33:53 +08:00

    已支持异步更新、Batch Update和新Lifecycle。

    reconciler.ts

    用于异步更新和Batch Update的调和器。调用requestAnimationFrame进行异步更新。根据kutId维护最小堆进行更新,因为kutId较小的节点会触发kutId较大的节点更新,故kutId以小到大进行更新,相同的节点更新进行合并实现Batch Update。

    接着该实现Context.....

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   940 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 20:56 · PVG 04:56 · LAX 12:56 · JFK 15:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.