摘要:要实施这个方案,最大问题就是接口约定。讨论地址是精读做了什么如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读帮你筛选靠谱的内容。
1 引言
setState 是 React 框架最常用的命令,它是用来更新状态的,这也是 React 框架划时代的功能。
但是 setState 函数是 react 包导出的,他们又是如何与 react-dom react-native react-art 这些包结合的呢?
通过 how-does-setstate-know-what-to-do 这篇文章,可以解开这个秘密。
2 概述setState 函数是在 React.Component 组件中调用的,所以最自然的联想是,更新 DOM 的逻辑在 react 包中实现。
但是 react 却可以和 react-dom react-native react-art 这些包打配合,甚至与 react-dom/server 配合在服务端运行,那可以肯定 react 包中不含有 DOM 更新逻辑。
所以可以推断,平台相关的 UI 更新逻辑分布在平台相关的包里,react 包只做了代理。
React 引擎不在 react 包里从 react 0.14 版本之后,引擎代码就从 react 包中抽离了,react 包仅仅做通用接口抽象。
也就是说,react 包定义了标准的状态驱动模型的 API,而 react-dom react-native react-art 这些包是在各自平台的具体实现。
各平台具体的渲染引擎实现被称为 reconciler,通过这个链接可以看到 react-dom react-native react-art 这三个包的 reconciler 实现。
这说明了 react 包仅告诉你 React 拥有哪些语法,而并不关心如何实现他们,所以我们需要结合 react 包与 react-xxx 一起使用。
对于 context,react 包仅仅会做如下定义:
// A bit simplified function createContext(defaultValue) { let context = { _currentValue: defaultValue, Provider: null, Consumer: null }; context.Provider = { $$typeof: Symbol.for("react.provider"), _context: context }; context.Consumer = { $$typeof: Symbol.for("react.context"), _context: context }; return context; }
具体用到时,由 react-dom 和 react-native 决定用何种方式实现 MyContext.Provider 这个 API。
这也说明了,如果你不同步升级 react 与 react-dom 版本的话,就可能碰到这样的报错:fail saying these types are invalid,原因是 API 定义与实现不匹配。
setState 怎么调用平台实现每个平台对 UI 更新逻辑的实现,会封装在 updater 函数里,所以不同平台代码会为组件添加各自的 updater 实现:
// Inside React DOM const inst = new YourComponent(); inst.props = props; inst.updater = ReactDOMUpdater; // Inside React DOM Server const inst = new YourComponent(); inst.props = props; inst.updater = ReactDOMServerUpdater; // Inside React Native const inst = new YourComponent(); inst.props = props; inst.updater = ReactNativeUpdater;
不同于 props, updater 无法被直接调用,因为这个 API 是由 react 引擎在 setState 时调用的:
// A bit simplified setState(partialState, callback) { // Use the `updater` field to talk back to the renderer! this.updater.enqueueSetState(this, partialState, callback); }
关系可以这么描述:react -> setState -> updater <- react-dom 等。
HooksHooks 的原理与 setState 类似,当调用 useState 或 useEffect 时,其内部调用如下:
// In React (simplified a bit) const React = { // Real property is hidden a bit deeper, see if you can find it! __currentDispatcher: null, useState(initialState) { return React.__currentDispatcher.useState(initialState); }, useEffect(initialState) { return React.__currentDispatcher.useEffect(initialState); } // ... };
ReactDOM 提供了 __currentDispatcher(简化的说法):
// In React DOM const prevDispatcher = React.__currentDispatcher; React.__currentDispatcher = ReactDOMDispatcher; let result; try { result = YourComponent(props); } finally { // Restore it back React.__currentDispatcher = prevDispatcher; }
可以看到,Hooks 的原理与 setState 基本一致,但需要注意 react 与 react-dom 之间传递了 dispatch,虽然你看不到。但这个 dispatch 必须对应到唯一的 React 实例,这就是为什么 Hooks 不允许同时加载多个 React 实例的原因。
和 updater 一样,dispatch 也可以被各平台实现重写,比如 react-debug-hooks 就重写了 dispatcher。
由于需要同时实现 readContext, useCallback, useContext, useEffect, useImperativeMethods, useLayoutEffect, useMemo, useReducer, useRef, useState,工程量比较浩大,建议了解基本架构就足够了,除非你要深入参与 React 生态建设。
3 精读与其他 React 分析文章不同,本文并没有过于刨根问题的上来就剖析 reconciler 实现,而是问了一个最基本的疑问:为什么 setState 来自 react 包,但实现却在 react-dom 里?React 是如何实现这个 magic 的?
通过这个疑问,我们了解了 React 更上层的抽象能力,如何用一个包制定规范,用 N 包去实现它。
接口的力量在日常编程中,接口也拥有的强大力量,下面举几个例子。
UI 组件跨三端的接口由于 RN、Weex、Flux 的某些不足,越来越多的人选择 “一个思想三端实现” 的方式做跨三端的 UI 组件,这样既兼顾了性能,又可以照顾到平台差异性,对不同平台组件细节做定制优化。
要实施这个方案,最大问题就是接口约定。一定要保证三套实现遵循同一套 API 接口,业务代码才可以实现 “针对任意一个平台编写,自动移植到其他平台”。
比较常用的做法是,通过一套统一的 API 文件约束,固定组件的输入输出,不同平台的组件做平台具体实现。这个思想和 React 如出一辙。
当然 RN 这些框架本身也是同一接口在不同平台实现的典型,只是做的不够彻底,JS 与 Native 的通信导致了性能不如源生。通用数据查询服务
通用数据查询服务也比较流行,通过磨平各数据库语法,让用户通过一套 SQL 查询各种类型数据库的数据。
这个方案中,一套通用的查询语法就类似 React 定义的 API,执行阶段会转化为各数据库平台的 SQL 方言。
小程序融合方案现在这种方案很火。通过基于 template 或者 jsx 的语法,一键发布到各平台小程序应用。
这种方案一定会抽象一套通用语法,甚至几乎等价与 react 与 react-dom 的关系:所有符合规范的语法,转化为各小程序平台的实现。
4 总结这种分平台实现方案与跨平台方案还是有很大区别的,像 JAVA 虚拟机本质还是一套实现方案。而分平台的实现可以带来最原生的性能与体验,同样收到的约束也最大,应该其 API 应该是所有平台支持的一个子集。
另外,这种方案不仅可以用于 一套规范,不同平台的实现,甚至可以用在 “同一平台的实现”。
无论是公司还是开源节界,都有许多重复的轮子或者平台,如果通过技术委员会约定一套平台的实现规范,大家都遵循这个规范开发平台,那未来就比较好做收敛,或者说收敛的第一步都是先统一 API 规范。
留下一个思考题:还有没有利用 setState 规范与实现分离的思想案例?欢迎留下你的答案。
讨论地址是:精读《setState 做了什么》 · Issue #122 · dt-fe/weekly
如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/100759.html
摘要:更容易将组件的与状态分离。也就是只提供状态处理方法,不会持久化状态。大体思路是利用共享一份数据,作为的数据源。精读带来的约定函数必须以命名开头,因为这样才方便做检查,防止用判断包裹语句。前端精读帮你筛选靠谱的内容。 1 引言 React Hooks 是 React 16.7.0-alpha 版本推出的新特性,想尝试的同学安装此版本即可。 React Hooks 要解决的问题是状态共享,...
摘要:今天我们就来解读一下的源码。比较有意思,将定时器以方式提供出来,并且提供了方法。实现方式是,在组件内部维护一个定时器,实现了组件更新销毁时的计时器更新销毁操作,可以认为这种定时器的生命周期绑定了组件的生命周期,不用担心销毁和更新的问题。 1. 引言 React PowerPlug 是利用 render props 进行更好状态管理的工具库。 React 项目中,一般一个文件就是一个类,...
摘要:精读源码一共行,我们分析一下其精妙的方式。更多讨论讨论地址是精读新用法如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读帮你筛选靠谱的内容。 1 引言 很高兴这一期的话题是由 epitath 的作者 grsabreu 提供的。 前端发展了 20 多年,随着发展中国家越来越多的互联网从业者涌入,现在前端知识玲琅满足,概念、库也越来越多。虽然内容越来越多,但作为个体的...
摘要:可以看到,这样不仅没有占用组件自己的,也不需要手写回调函数进行处理,这些处理都压缩成了一行。效果通过拿到周期才执行的回调函数。实现等价于的回调仅执行一次时,因此直接把回调函数抛出来即可。 1 引言 上周的 精读《React Hooks》 已经实现了对 React Hooks 的基本认知,也许你也看了 React Hooks 基本实现剖析(就是数组),但理解实现原理就可以用好了吗?学的是...
摘要:未来可能成为官方之一。讨论地址是精读组件如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读帮你筛选靠谱的内容。 1. 引言 为什么要了解 Function 写法的组件呢?因为它正在变得越来越重要。 那么 React 中 Function Component 与 Class Component 有何不同? how-are-function-components-di...
阅读 916·2021-11-08 13:22
阅读 2841·2021-09-29 09:45
阅读 2823·2021-09-09 11:52
阅读 2257·2019-08-30 13:20
阅读 3739·2019-08-29 13:28
阅读 1355·2019-08-29 12:32
阅读 2719·2019-08-29 11:10
阅读 1643·2019-08-26 13:34