在 React 仓库中的那些 package

发布于: 2020 年 06 月 20 日
在 React 仓库中的那些 package

翻翻 React 源码仓库的时候会发现里边的 package 还是很多的,记录几个感觉比较有用的,抛砖引玉。

react

只要你用 react 的话就会用到的一个 package,像是没有什么好聊的样子,这里附上官网的 API 文档:https://reactjs.org/docs/react-api.html

仔细看看官网齐全的 API 文档也是很有收获的,可能会发现一些好用但是你之前并不知道的 API,或者了解一些原理性上边的东西。

例如我之前都不知道有个 React.Children 工具集合可以协助处理一些 props.children 迭代的问题。如果继续稍稍深入挖掘一下,可以打开这个 package 的入口文件,看看它究竟暴露了哪些东西给我们。

我们拿 Tag: v16.3.1 的代码来看的话,会发现除了文档中提到的,还是多了蛮多其他的 API 的,这里简单聊几个比较有意思的。

下边三个是和 Concurrent UI 模式有关的 API,详细内容参考:https://reactjs.org/docs/concurrent-mode-patterns.html,嗯,也是试验阶段的。

  • useTransition

  • useDeferredValue

  • SuspenseList

这三个 API 都挺难一句话总结的,后续有机会在仔细聊聊 concurrent mode 的东西吧。

react-dom

react 刚开始时以它的 Virtual DOM 概念出名,除了解决一部分的 DOM 操作性能问题外,最重要的意义应该是提供了一个 UI 抽象层,在这一层的基础上可以去适配多个平台的应用,而我们最常见的 Web 的渲染实现便是 react-dom 这个库。

react-dom 对于开发者来说用得最多的应该就是 render()createPortal() 了。

react-dom 是基于 react-reconciler(后边再聊一下这个很重要的 package) 实现的一个浏览器的渲染层,里边提供了在浏览器各种场景下操作 DOM 的方法,以及跨浏览器兼容的 synthetic events 支持,涉及到的实现细节是十分复杂的,你在使用过程中也会发现这个库体积也是蛮大的。

我们在使用 react-dom 的事件时,有时候会用 event.nativeEvent 来获取浏览器底层真实的事件对象。react-dom 应该是使用了事件委托的,并且事件对象都是经过一层封装的,甚至实现了一套事件插件机制,下图是从源码中翻出来的注释:

/**
* Overview of React and the event system:
*
* +------------+ .
* | DOM | .
* +------------+ .
* | .
* v .
* +------------+ .
* | ReactEvent | .
* | Listener | .
* +------------+ . +-----------+
* | . +--------+|SimpleEvent|
* | . | |Plugin |
* +-----|------+ . v +-----------+
* | | | . +--------------+ +------------+
* | +-----------.--->|PluginRegistry| | Event |
* | | . | | +-----------+ | Propagators|
* | ReactEvent | . | | |TapEvent | |------------|
* | Emitter | . | |<---+|Plugin | |other plugin|
* | | . | | +-----------+ | utilities |
* | +-----------.--->| | +------------+
* | | | . +--------------+
* +-----|------+ . ^ +-----------+
* | . | |Enter/Leave|
* + . +-------+|Plugin |
* +-------------+ . +-----------+
* | application | .
* |-------------| .
* | | .
* | | .
* +-------------+ .
* .
* React Core . General Purpose Event Plugin System
*/

貌似最新的版本对这一块的内容有一些改造,有空我们再深挖吧。

对了,react-dom 还提供了一个用于测试组件的工具库,如果你需要做 react 组件相关的 UI 测试,建议仔细看一下,应该会有收获:https://reactjs.org/docs/test-utils.html

react-is

这个库用来判断 react 的元素类型,提供的 API 很简单,看名字就会用了,在某些场景下也很有用,这里简单记录一下:

  • isValidElementType

  • typeOf

  • isContextConsumer

  • isContextProvider

  • isElement

  • isFragment

  • isPortal

  • isStrictMode

react-test-renderer

如果使用 jest 写过 react 组件相关的测试用例,便会对这个 package 有一定的了解,它可以帮我们把 react 组件转变为更纯粹的 JavaScript 对象,也就是可以 toJSON ,依旧是搬运 README 里的例子:

const ReactTestRenderer = require('react-test-renderer');
const renderer = ReactTestRenderer.create(
<Link page="https://www.facebook.com/">Facebook</Link>
);
console.log(renderer.toJSON());
// { type: 'a',
// props: { href: 'https://www.facebook.com/' },
// children: [ 'Facebook' ] }

在 react 没有合适的 UI 渲染环境宿主时,可以用它来生成组件的 UI 快照,最大的好处就是便于我们在 Node 环境里做 UI 比对测试。

除了 create 外,它还提供了其他的一些有用 API,有兴趣可以详细阅读一下官方的文档:https://reactjs.org/docs/test-renderer.html

react-reconciler

这个 package 可以让我们来自定义 react 的渲染层,例如 react-dom 或者 react-native,假设你要用 react 在浏览器 canvas 上渲染内容,便可以使用 react-reconciler 自定义一个 renderer。这个就是 react Virtual DOM 强大所在,你可以在 react 这种编写 UI 的范式上构建任意平台的渲染层,做到 learn once, write anywhere。

这里搬运一下 package 里 README 的使用例子:

const Reconciler = require('react-reconciler');
const HostConfig = {
// You'll need to implement some methods here.
// See below for more information and examples.
};
const MyRenderer = Reconciler(HostConfig);
const RendererPublicAPI = {
render(element, container, callback) {
// Call MyRenderer.updateContainer() to schedule changes on the roots.
// See ReactDOM, React Native, or React ART for practical examples.
}
};
module.exports = RendererPublicAPI;

这个例子太简单了,通过定义一个 HostConfig 来设置 render 时调用的各种方法,然后创建一个 renderer,再对外提供一个 root 的 render 方法,就像 ReactDOM.render 一样就可以了。

render 方法里通常是创建一个 root container ,然后调用 renderer.updateContainer(...) 方法即可。

我们好奇的是 HostConfig 应该怎么写,这里边要定义的东西还蛮多的,下边简单列一下:

  • now

  • getRootHostContext

  • prepareForCommit

  • resetAfterCommit

  • getChildHostContext

  • shouldSetTextContent

  • createInstance

  • createTextInstance

  • finalizeInitialChildren

  • appendInitialChild

  • appendChild

  • removeChild

  • appendChildToContainer

  • prepareUpdate

  • commitUpdate

  • commitTextUpdate

  • removeChild

  • supportsMutation

这里不展开讲上述各个属性的作用,大部分都是方法,定义 react 生命周期中渲染层要做什么事情,这里放一篇详细介绍如何使用的 react-reconciler 的文章:https://www.notion.so/react-package-51ae5948612c4156a78d05016269883a#2b7e491bf51c42acb3d6d0b3f0826182

这篇文章里有作者写的一个例子,可以通过例子很快上手 react-reconciler 的使用。

scheduler

之前大街小巷都在传,react 16 版本使用了 fiber 的架构。react 的 fiber 架构会对原有的 render 流程进行更加细化的任务拆分,然后基于 scheduler 来做时间分片和任务调度,通过任务优先级来更好地控制 UI 更新,避免出现掉帧。

在 react 官方文档的设计原则中提到,基于数据渲染 UI 的流程对应用开发者来说希望是透明且高效的,react 会去尝试推迟那些优先级低的 UI 更新,来保证当前的 UI 体验更加流畅。

react 会用一种类似 pull 的形式,确定更新必要时再做变更,这一块其实还有很多可以想象的空间:https://reactjs.org/docs/design-principles.html#scheduling

很可惜的是,这个 package 的 README 中提到,scheduler 用于 react 内部,我也找不到更加详细的 API 介绍,详细的使用说明得深入到 react fiber 的执行流程中去摸索,但其实社区上蛮多相关文章的,有兴趣的朋友可以找来阅读一下。

发布于: 2020 年 06 月 20 日 阅读数: 8
用户头像

teabyii

关注

我应该静下心来 2017.10.17 加入

还未添加个人简介

评论

发布
暂无评论
在 React 仓库中的那些 package