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 ...
随机推荐
- CoreData和FMDB你用哪个?
概括: 我们先说说这两个东西,CoreData 和 FMDB,其实就我自己而言觉得这两个都不错,刚开始是接触FMDB的,CoreData是工作后自己看的.苹果推荐开发者去使用CoreData,但 FM ...
- hibernate与mybatis的区别
我是一名java开发人员,hibernate以及mybatis都有过学习,在java面试中也被提及问道过,在项目实践中也应用过,现在对hibernate和mybatis做一下对比,便于大家更好的理解和 ...
- 《浅谈架构之路:单点登录 SSO》
前言:SSO 单点登录 “半吊子”的全栈工程师又来了,技术类的文章才发表了两篇,本来想先将主攻的几个系列都开个头(Nodejs.Java.前端.架构.全栈等等),无奈博客起步太晚,写博文的时间又没有很 ...
- Javascript 闭包与高阶函数 ( 一 )
上个月,淡丶无欲 让我写一期关于 闭包 的随笔,其实惭愧,我对闭包也是略知一二 ,不能给出一个很好的解释,担心自己讲不出个所以然来. 所以带着学习的目的来写一写,如有错误,忘不吝赐教 . 为什么要有闭 ...
- [Netty] - Netty入门(最简单的Netty客户端/服务器程序)
Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...
- 关于OpenGL和DX学习的取舍
大家多知道左右就肯定要与显卡打交道.两大图形图像IPA.OpenGL(图形),DX(图形,声音,键盘控制,网络) OpenGL的兴起可能取决于苹果公司的适用,吸引看大部分开发者适用,它有跨平台的有点. ...
- 2017-2-20 C#基础 运算符
C#的运算符主要分五种:算数运算符,关系运算符,逻辑运算符,条件运算符,赋值运算符.算术运算符有 + - * / % ++ --;关系运算符有 == != > ...
- MongoDB【第一篇】安装
第一步:准备 1. 操作系统 CentOS-7-x86_64-Everything-1511 2. MongoDB 版本 mongodb-linux-x86_64-rhel70-3.4.2 3. 设置 ...
- 每天一个linux命令(33)--df命令
周末是学习的好时光. 每天一个Linux命令,接下来是磁盘存储相关的,df, du命令 Linux中 df 命令的功能是用来检查Linux 服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬 ...
- 转 jquery怎么在header中设置请求信息
jquery是js的类库,js本身不能操作header,因为js是在浏览器加载页面过程中才开始执行的 header需要服务器端执行操作 如果是ajax,是可以设置header $.ajax({ url ...