React 16.3新API

createContext

之前也有,相当于自动向下传递的,子树中的任意组件都可以从中按需取值(配合声明)

像一样,的作用也是自上而下传递数据,通常用于多语言配置、主题和数据缓存等场景,这些场景有几个特点:

同一份数据需要被多个组件访问

这些组件处于不同的嵌套层级

从数据传递的角度看,是一级数据共享,是子树共享。如果没有特性的话,就需要从数据源组件到数据消费者组件逐层显式传递数据(),一来麻烦,二来中间组件没必要知道这份数据,逐层传递造成了中间组件与数据消费者组件的紧耦合。而特性能够相对优雅地解决这两个问题,就像是机制的补丁

P.S.实际上,要解耦中间组件与数据消费者组件的话,还有另一种方法:把填好数据的组件通过传递下去,而不直接传递数据。这样中间组件就不需要知道数据消费者组件的内部细节(如依赖的数据)了,只知道这个位置将被插入某个组件(也就是组件组合,类似于Vue的特性),这种思路有点IoC的意思,具体见Before You Use Context

API算是对特性的重新实现(可替代之前的):

P.S.旧的 API在仍然可用,但之后会被移除掉

只维护(没有),创建时给定默认值,通过组件写,通过组件来读

一个可以对应多个,内层能够重写外层的值(实际上会从组件树中与之匹配的最近那里拿到值),的 发生变化时会通知所有后代重新渲染(直接通知,不走)

P.S.默认值比较有意思,如果没有与之匹配的,就走。作用是在单测等场景,可以不需要自己跑

P.S.比较新旧,确定是否发生了变化,走的是Object.is()浅对比逻辑(引用类型只比较引用)

内部实现

类型定义如下:

看起来比较奇怪,带两份等值属性是为了支持多并发工作(使之互不影响):

As a workaround to support multiple concurrent renderers, we categorize some renderers as primary and others as secondary. We only expect there to be two concurrent renderers at most: React Native (primary) and Fabric (secondary); React DOM (primary) and React ART (secondary). Secondary renderers store their context values on separate fields.

和两个属性很有意思,存在循环引用:

用来校验和组件是否匹配:

实现如下:

在渲染阶段把组件身上的 转移到对象上:

读取时建立依赖关系:

fiber节点上带有依赖链表,的发生变化时通知所有依赖项,大致如下:

P.S.具体实现细节见react/packages/react-reconciler/src/ReactFiberNewContext.js

此外还有两种组件,与:

看起来比较特殊,其是个的函数

createRef

之前版本中,有2种形式:

字符串形式

函数形式

示例:

前者方便易用,后者更安全(时候会给掉,游离节点引发的内存风险降低不少)

此外,字符串还有很多缺陷:

要兼容Closure Compiler高级模式的话,必须把标识为字符串(具体见Types in the Closure Type System)

不允许单一实例有多个owner

动态字符串会妨碍VM优化

在异步批量渲染下存在问题,因为是同步处理的,需要始终保持一致

可以通过hook获取到兄弟,但破坏了组件的封装性

不支持静态类型化,在类似TypeScript的(强类型)语言中,每次用到都必须显式转换

由子组件调用的回调中无法把绑定到正确的上,例如中的会被挂在执行改回调的组件上,而不是当前

希望能够传递,能有多个,以及适应异步批处理场景……关于此话题的更多讨论,见Implement Better Refs API

第3种不是字符串也不是函数,而是个对象(故称之为对象ref):

也就是说:

这里给指定的属性,实际上是个对象(身上有个属性),所以用法是这样:

就实现而言,与之前的字符串相比,不过是包了一层对象而已。其类型定义如下:

P.S.其中的Flow类型定义表示禁止扩展()

是仅含一个 的对象,这样做有3个好处:

相对安全。与函数类似,时会被置为,一定程度上降低了内存风险

适用于函数式组件。因为对象不与组件实例强关联(不要求创建实例,函数也具有这个优势)

可传递,也能有多个。这一点比函数和字符串都强大,反正只是个对象,多个组件持有也没关系,比其它两个灵活

P.S.之所以说“一定程度上”,是因为非要这么干的话,肯定是不掉的(包的这一层引用隔离,可以轻易突破)

P.S.虽然有了新的对象,但并没有废弃前两个,3者目前的状态是:

对象:因可传递等特性,建议使用

函数:因其灵活性而得以保留,建议使用

字符串:不建议使用,并且在后续版本可能被移除掉

函数形式的提供了更细粒度的控制(fine-grain control),包括绑定、解绑的时机

P.S.对象很大程度上是作为字符串的替代品推出的,所以建议用对象,废弃字符串

forwardRef

大多数场景用不着,但在几个典型场景很关键:

触发深层的(如自动聚焦搜索框)

计算元素宽高尺寸(如JS布局方案)

重新定位DOM元素(如)

从组件角度分为两类:

DOM包装组件

高阶组件(High Order Component)

上面提到的3个场景都属于DOM包装组件,比如、、,特点是对DOM节点的包装/增强。从使用角度看,与等原生DOM节点地位一样,能构成视图,并且可交互。而交互的支持依赖对原生DOM节点的控制,比如无论包多少层,想要效果的话,最终还是要触发节点的对应行为,这种场景下,传递就成了刚需

These components tend to be used throughout the application in a similar manner as a regular DOM button and input, and accessing their DOM nodes may be unavoidable for managing focus, selection, or animations.

P.S.实际应用中,甚至见到过类似的奇技淫巧,这实际上就是对传递特性的强烈需求

而高阶组件一般是对组件功能的增强/扩展,因此天生就面临传递的问题,包了一层之后就不能直接访问了,但又没有太好的方式向下传递,所以一直是个问题(以不太优雅的方式维持链)

不使用 API的话,可以这样解决:

(摘自gaearon/dom_ref_forwarding_alternatives_before_16.3.md)

姑且称之为别名ref prop传递,说白了就是通过向下传递一个载体(),到达目标节点后与之关联起来(),类似于:

API提供了一种比较优雅的解决方案:

对比上面第一种替代方案,几乎一模一样,无非是把作为独立参数,从而避免用不叫的传递的尴尬

在高阶组件的场景,这样做:

因为接受一个函数,非常适合函数式组件,而对形式的组件不太友好,所以上例这样的高阶函数场景,实质上是通过来解决的

内部实现

与载体的思路几乎没什么区别,甚至其内部实现也差不多

先看API入口:

接受一个类型的函数作为参数,返回值是一种新的(即合法,用来描述视图结构的对象),相当于给这参数传入的函数添上了类型标识

P.S.更多合法见react/packages/shared/isValidElementType.js

内部根据该类型标识区分出来之后,做一些额外处理,包括挂载、更新和卸载3部分:

(摘自react/packages/react-reconciler/src/ReactFiberBeginWork.js、react/packages/react-reconciler/src/ReactFiberCommitWork.js,清晰起见,不太重要的部分都删掉了)

挂载阶段实际上并不关心对象的来源(无论层层传递过来的还是自己创建的都一样),更新也没什么特殊的,用新的和去,卸载就是置,实现其实比较简单

StrictMode

StrictMode is a tool for highlighting potential problems in an application.

用来开启子树严格检查,是个内置组件:

有几个特点:

不渲染UI,像一样

会为后代组件(即子树级)开启额外的检查和警告提示

仅在环境有效,不影响版本

主要有4个作用:

识别具有生命周期的组件

字符串警告

检测非预期的副作用

检测旧的 context API

P.S.以后还会添加更多功能

、字符串、旧 API检查的实际意义是保障API废弃决策可靠推进,尤其是涉及第三方依赖的场景,很难确认是否存在即将过时的API的使用,提供运行时检查能够有效提醒开发者去处理,例如:

而副作用检测对于Async Rendering特性是很有意义的,第一阶段涉及很多组件方法:

也就是说,这些函数将来(开启异步渲染特性之后)可能会被调用多次,所以要求不含副作用(即idempotent,调用多次和调用一次产生的效果完全一样)。但问题是,副作用很难被检测到,也做不到,所以做了这样一件事情:

By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot.

具体地,故意多调1次这些函数:

组件的构造函数

函数

传入的更新函数

生命周期函数

算是多少有点帮助吧,既然无法帮助解决问题,那就想办法帮助暴露问题

参考资料

Refs and the DOM

Strict Mode

React v16.3.0: New lifecycles and context API

联系ayqy

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180805G1CU2G00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券