原文链接-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架构与React Fiber的更多相关文章

  1. React Fiber源码分析 (介绍)

    写了分析源码的文章后, 总觉得缺少了什么, 在这里补一个整体的总结,输出个人的理解~ 文章的系列标题为Fiber源码分析, 那么什么是Fiber,官方给出的解释是: React Fiber是对核心算法 ...

  2. react fiber

    react fiber https://github.com/acdlite/react-fiber-architecture https://github.com/facebook/react/is ...

  3. 瞧一瞧React Fiber

    啥是React Fiber? React Fiber,简单来说就是一个从React v16开始引入的新协调引擎,用来实现Virtual DOM的增量渲染. 说人话:就是一种能让React视图更新过程变 ...

  4. React Fiber 数据结构揭秘

    此章节会通过两个 demo 来展示 Stack Reconciler 以及 Fiber Reconciler 的数据结构. 个人博客 首先用代码表示上图节点间的关系.比如 a1 节点下有 b1.b2. ...

  5. React Fiber源码分析 第二篇(同步模式)

    先附上两张流程图 1.scheduleRootUpdate  这个函数主要执行了两个操作  1个是创建更新createUpdate并放到更新队列enqueueUpdate, 1个是执行sheculeW ...

  6. React Fiber源码分析 第一篇

    先附上流程图一张 先由babel编译, 调用reactDOM.render,入参为element, container, callback, 打印出来可以看到element,container,cal ...

  7. 如何架构一个 React 项目?

    编程有点像搞园艺.比起竭力去对付BUG(虫子),我们更愿意把一切弄得整洁有序,以免最后落得个身在荒野丛林中.低劣的架构会拖我们的后腿,也会使得BUG更容易钻进系统里去. 想要对你的项目进行架构,方法有 ...

  8. React Fiber源码分析 第三篇(异步状态)

    先附上流程图~ 调用setState时, 会调用classComponentUpdater的enqueueSetState方法, 同时将新的state作为payload参数传进 enqueueSetS ...

  9. (架构)React Native 导出项目全局共用组件的模块

    自定义组件全局使用(类似如下) import { ReactNavComponent, Widget, Util } from 'rn-yunxi'; const { RegexpUtil, Stor ...

随机推荐

  1. CoreData和FMDB你用哪个?

    概括: 我们先说说这两个东西,CoreData 和 FMDB,其实就我自己而言觉得这两个都不错,刚开始是接触FMDB的,CoreData是工作后自己看的.苹果推荐开发者去使用CoreData,但 FM ...

  2. hibernate与mybatis的区别

    我是一名java开发人员,hibernate以及mybatis都有过学习,在java面试中也被提及问道过,在项目实践中也应用过,现在对hibernate和mybatis做一下对比,便于大家更好的理解和 ...

  3. 《浅谈架构之路:单点登录 SSO》

    前言:SSO 单点登录 “半吊子”的全栈工程师又来了,技术类的文章才发表了两篇,本来想先将主攻的几个系列都开个头(Nodejs.Java.前端.架构.全栈等等),无奈博客起步太晚,写博文的时间又没有很 ...

  4. Javascript 闭包与高阶函数 ( 一 )

    上个月,淡丶无欲 让我写一期关于 闭包 的随笔,其实惭愧,我对闭包也是略知一二 ,不能给出一个很好的解释,担心自己讲不出个所以然来. 所以带着学习的目的来写一写,如有错误,忘不吝赐教 . 为什么要有闭 ...

  5. [Netty] - Netty入门(最简单的Netty客户端/服务器程序)

    Java中的NIO是一种解决阻塞式IO问题的基本技术,但是NIO的编写对java程序员是有比较高的要求的.那么Netty就是一种简化操作的一个成熟的网络IO编程框架.这里简单介绍一个程序,代码是< ...

  6. 关于OpenGL和DX学习的取舍

    大家多知道左右就肯定要与显卡打交道.两大图形图像IPA.OpenGL(图形),DX(图形,声音,键盘控制,网络) OpenGL的兴起可能取决于苹果公司的适用,吸引看大部分开发者适用,它有跨平台的有点. ...

  7. 2017-2-20 C#基础 运算符

    C#的运算符主要分五种:算数运算符,关系运算符,逻辑运算符,条件运算符,赋值运算符.算术运算符有 + - * /  %    ++    --;关系运算符有    ==    !=    >   ...

  8. MongoDB【第一篇】安装

    第一步:准备 1. 操作系统 CentOS-7-x86_64-Everything-1511 2. MongoDB 版本 mongodb-linux-x86_64-rhel70-3.4.2 3. 设置 ...

  9. 每天一个linux命令(33)--df命令

    周末是学习的好时光. 每天一个Linux命令,接下来是磁盘存储相关的,df, du命令 Linux中 df  命令的功能是用来检查Linux 服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬 ...

  10. 转 jquery怎么在header中设置请求信息

    jquery是js的类库,js本身不能操作header,因为js是在浏览器加载页面过程中才开始执行的 header需要服务器端执行操作 如果是ajax,是可以设置header $.ajax({ url ...