Omi架构与React Fiber
原文链接-https://github.com/AlloyTeam/omi/tree/master/tutorial
写在前面
Omi框架在架构设计的时候就决定把update的控制权交给了开发者,视灵活性比生命还重要。不然的话,如果遇到React Fiber要解决的这类问题的话,就需要推翻原有架构重新搞了。
React Fiber
先引用下我们团队小鲜肉Stark伟-复旦大四 / 腾讯@AlloyTeam在知乎上的回答
React 的核心思想是每次对于界面 state 的改动,都会重新渲染整个 virtual dom,然后新老的两个 virtual dom 树进行 diff,对比出变化的地方,然后通过 renderer 渲染到实际的UI界面(这里可能是浏览器的DOM,也可能是native组件)。这样实质上就是把界面变成一个纯粹的状态机,React 的作用就是把这个状态机之间的状态转换高效率地运行出来。但是存在以下问题:
- 1、不是每一次状态的变化都要立刻执行。
- 2、不同的状态变化之间是有轻重缓急之分的,比如『动画』这种状态变化的优先级,出于对用户体验的考量,为了避免动画卡顿或者掉帧,一般比『改变页面数据』的优先级更高。
- 3、我们现在的做法只是调用 setState 触发重新渲染,然后 React 会收集一个 tick 内的 state 变化,然后执行,所以有可能大量的计算会在同一时刻阻塞进程。但我们没法控制 React 运算的时序问题,也不太可能通过手工声明让动画的优先级比数据变更更高。而 React 作为一个用户交互的框架,它本应该能让程序员能控制这些东西。所以这个破事要怎么解决咧?( ⊙ o ⊙ )我们知道,任何的函数调用都会有自己的调用栈,比如对于 v = f(d) 这个函数来说,函数 f 可能又调用了一系列其它的函数,这些函数就包括在 f 的调用栈中。关键的问题在于,这种原生的调用栈是基本不可延迟的,它会立即执行,如果计算量很大的话就会阻塞住进程,让界面失去响应,这种事情经常发生在 React 的渲染过程中。
或者看颜什么都不记得适的回答:
状态转移时,是在一次 tick 中递归遍历组件树,找出需要更新的节点 rerender。但是这样造成了一些问题:
- 在 UI 中,不是所有的状态转移都需要立即执行。大量的同时计算可能会导致资源的浪费,以致出现掉帧的状况,降低用户体验。
- 不同类型的状态转移应有不同的优先级,比如点击按钮出现动画的优先级应该比 Fetch API 要高。
- React 是 pull-based 实现的,事务的时序全部由 React 决定。我们没办法操控执行事务的时序。
Omi component update
Omi有上面的问题吗? 没有。
Omi的卖点之一便是:更自由的更新,每个组件都有update方法,自由选择你认为最佳的时机进行更新。这样设计的一大好处是更加灵活,如果想要自动更新集成个mobx或者obajs便可,进可功退可守护。
数据和视图虽然是关系密切,但是解耦的设计还是非常必要,这样可以应付更多的场景。好处:
- 你可以等某个动画播放完成再进行update
- 你可以控制update顺序
- 你可以update前后干一些事情而不需要利用生命周期的钩子函数(有的时候钩子函数让连续的逻辑打得粉碎...)
component update说完了吗?没有... Omi不仅仅有component update!还有更加强大的 updateSelf。
Omi component updateSelf
先说下两者的区别:
- update: 更新组件树
- updateSelf: 更新组件(不包含任何子节点)
如下图所示:


标红的代表会进行更新的节点。
场景模拟
class TestComponent extends Omi.Component {
render () {
return `<div>
<h3>{{title}}</h3>
<List name="list" data="listData" />
</div>`;
}
}
组件结构上面代码所示:
- 如果调用组件实例的update的话,会更新组件本身以及 List组件
- 如果调用组件实例的updateSelft的话,会更新组件本身,不会更新List组件
比如我们仅仅修改了this.data.title,就可以调用this.updateSelf方法,虽然一般情况下无脑update也能达到同样的结果,虽然morphdom的DOM diff已经足够轻量快速,但是一定没有updateSelf方法快速。上面的例子updateSelf优势可能不明显,如果这样呢:
class TestComponent extends Omi.Component {
render () {
return `<div>
<h3>{{title}}</h3>
<List name="list" data="listData" />
<List name="list" data="listData" />
<Content name="list" data="listData" />
<Slider name="list" data="listData" />
</div>`;
}
}
再或者Content、Slider里面再嵌套了子组件,子组件又嵌套了子组件,如果仅仅只是需要修改title的话,updateSelf优势就尽显无疑。
实现细节
这里主要说一说updateSelf的实现细节。主要包含两点:
- 不重新render的情况下拿到子组件的完整的HTML
- 关闭子组件的DOM diff
进行updateSelf的时候,就算子组件的data发生了变化,也不去改变子组件。因为updateSelf就意思就是更新自身。
所以子组件的HTML不需要使用模板和data生成,只需要component.node.outerHTML就可以了。outerHTML在古老的firefox是不支持的,可以通过创建节点插入然后读innerHTML进行polyfill。
组件本身的HTML是需要使用模板和data生成,子组件就使用刚刚的outerHTML替换便可。但是问题来了,子组件的DOM diff其实是没有必要的,虽然morphdom的DOM diff已经足够轻量快速。但是子组件他们本来就是一模一样,没有必要的开销。所以需要关闭DOM diff~~。然后morphdom没有ignore相关的配置....
扩展 morphdom
API:
morphdom(node, newNodeHTML, {
ignoreAttr: ['attr1','attr2']
} )
比如上面代表只要标记了attr1或者attr2的就是忽略,当然为了规避错误,这里需要严格的匹配才会ignore DOM diff。怎么算严格的匹配?就是:
- 当同样的attr的DOM,并且该attr在ignoreAttr里才会ignore DOM diff
Omi Store体系addSelfView
Omi Store体系以前通过addView进行视图收集,store进行update的时候会调用组件的update。
与此同时,Omi Store体系也新增了addSelfView的API。
- addView 收集该组件视图,store进行update的时候会调用组件的update
- addSelfView 收集该组件本身的视图,store进行update的时候会调用组件的updateSelf
当然,store内部会对视图进行合并,比如addView里面加进去的所有视图有父子关系的,会把子组件去掉。爷孙关系的会把孙组件去掉。addSelfView收集的组件在addView里已经收集的也去进行合并去重,等等一系列合并优化。
Omi相关
- Omi官网omijs.org
- Omi的Github地址https://github.com/AlloyTeam/omi
- 如果想体验一下Omi框架,可以访问 Omi Playground
- 如果想使用Omi框架或者开发完善Omi框架,可以访问 Omi使用文档
- 如果你想获得更佳的阅读体验,可以访问 Docs Website
- 如果你懒得搭建项目脚手架,可以试试 omi-cli
- 如果你有Omi相关的问题可以 New issue
- 如果想更加方便的交流关于Omi的一切可以加入QQ的Omi交流群(256426170)

Omi架构与React Fiber的更多相关文章
- React Fiber源码分析 (介绍)
写了分析源码的文章后, 总觉得缺少了什么, 在这里补一个整体的总结,输出个人的理解~ 文章的系列标题为Fiber源码分析, 那么什么是Fiber,官方给出的解释是: React Fiber是对核心算法 ...
- react fiber
react fiber https://github.com/acdlite/react-fiber-architecture https://github.com/facebook/react/is ...
- 瞧一瞧React Fiber
啥是React Fiber? React Fiber,简单来说就是一个从React v16开始引入的新协调引擎,用来实现Virtual DOM的增量渲染. 说人话:就是一种能让React视图更新过程变 ...
- React Fiber 数据结构揭秘
此章节会通过两个 demo 来展示 Stack Reconciler 以及 Fiber Reconciler 的数据结构. 个人博客 首先用代码表示上图节点间的关系.比如 a1 节点下有 b1.b2. ...
- React Fiber源码分析 第二篇(同步模式)
先附上两张流程图 1.scheduleRootUpdate 这个函数主要执行了两个操作 1个是创建更新createUpdate并放到更新队列enqueueUpdate, 1个是执行sheculeW ...
- React Fiber源码分析 第一篇
先附上流程图一张 先由babel编译, 调用reactDOM.render,入参为element, container, callback, 打印出来可以看到element,container,cal ...
- 如何架构一个 React 项目?
编程有点像搞园艺.比起竭力去对付BUG(虫子),我们更愿意把一切弄得整洁有序,以免最后落得个身在荒野丛林中.低劣的架构会拖我们的后腿,也会使得BUG更容易钻进系统里去. 想要对你的项目进行架构,方法有 ...
- React Fiber源码分析 第三篇(异步状态)
先附上流程图~ 调用setState时, 会调用classComponentUpdater的enqueueSetState方法, 同时将新的state作为payload参数传进 enqueueSetS ...
- (架构)React Native 导出项目全局共用组件的模块
自定义组件全局使用(类似如下) import { ReactNavComponent, Widget, Util } from 'rn-yunxi'; const { RegexpUtil, Stor ...
随机推荐
- Java生成、解析二维码
今天遇到需求,使用Java生成二维码图片,网搜之后,大神们早就做过,个人总结一下. 目标:借助Google提供的ZXing Core工具包,使用Java语言实现二维码的生成和解析. 步骤如下: 1.m ...
- 可能是一份没什么用的爬虫代理IP指南
写在前面 做爬虫的小伙伴一般都绕不过代理IP这个问题. PS:如果还没遇到被封IP的场景,要不就是你量太小人家懒得理你,要不就是人家压根不在乎... 爬虫用户自己是没有能力维护一系列的代理服务器和代理 ...
- Qt 地址薄 (一) 界面设计
实现一个简单的地址薄,功能包括:地址的添加.浏览.编辑.查找.输出文件等. 1 界面和元素 整个地址薄界面,可视为一个 AddressBook 类.其中的 Name.Address 以及两个编辑栏, ...
- STM32F4的FPU单元讲解
搞STM32F407单片机的时候 看见的关于STM32F4系列的FPU 单元讲解 比较精彩的博客 于是特意转载 和大家分享 转自:http://blog.renren.com/blog/256814 ...
- PAT---完美数列
由于数值比较大,选用long型的 先用快速排序方法对数组进行排序,然后进行查找. 用一个问题是所要查找的数可能不在数组中,因此不能用现成的二叉查找法.试着对二叉查找法进行改进,单没调通. 在查找过程中 ...
- Android Realm数据库使用指南
Android Realm数据库使用指南 Realm数据库, 目前有Java, Objective‑C, React Native, Swift, Xamarin的几种实现, 是一套用来取代SQLit ...
- AR入门系列-05-Vuforia识别目标视频播放
在识别目标后播放视频我们需要一个插件 Easy Movie Texture 2.36.unitypackage 百度网盘下载地址:http://pan.baidu.com/s/1skT8Xp7 将Ea ...
- .NET Core 1.1日期解析无APi、SQL Server数据转换JSON
前言 在批量导入Excel中的数据时发现出生日期为整数也就是为天数,结果倒腾了翻,这是其一,其二是数据库中的某一列存的是JSON数据,但是场景是为了作为作业来运行,此时不得不将筛选出的数据手动拼接成J ...
- 从零开始学习Vue(三)
我们从一个例子来学习组件,vuejs2.0实战:仿豆瓣app项目,创建自定义组件tabbar 这个例子用到其他组件,对于初学者来说,一下子要了解那么多组件的使用,会变得一头雾水.所以我把这个例子改写了 ...
- Implement Stack using Queues leetcode
Implement the following operations of a stack using queues. push(x) -- Push element x onto stack. po ...