进击ReactNative-徐如林-React源码解析

阅读原文

有的人可能会不理解,大前端平台化的战火为谁而燃,吾辈何以为战?
专注于移动互联网大前端致富,一直是我们最崇高的理想,而ReactNative是横亘在中间的桥头堡。
纵观行业风向,有作壁上观者,有磨刀霍霍者,有从入门到放弃者,有大刀阔斧者,但是缺乏深潜微操者。
啊哈,是时候该我出手了。
祭出“大海航术”,经过一年来不懈钻研,基于React Developer Tools研发插件,实时绘制运行时三棵树–Fiber双树Native View树React方法调用树,在上帝视角和时间旅行的引领下,冲破波诡云谲的Fiber算法迷航,日照大海现双龙。

本文主要针对ReactNative(以下简称 RN)的React.js源码进行分析,先说清楚开发者接触到的API,然后再深挖对应底层实现逻辑,最后找找微操的空间。如果有对RN不太熟悉的朋友,建议看一下《进击ReactNative-疾如风》热热身,该文从“原理+实践,现学现做”的角度手写石器时代RN,粗线条描述跨平台套路,迂回包抄,相对比较轻松!本文则正面刚React源码,略显烧脑。

话说,做大事,就要用大斧头。先耍耍阿里“三板斧”撼动一下。

定目标

传道(攻坚方法论)

近几年的移动互联网北漂生涯,给我结结实实的上了一课:人生,除了发财,就是不断探索、抽象、践行、强化自己的方法论、稳固自己的价值观,过程呈螺旋式上升,只要把握住总结复盘的秘诀,成长就很快乐。

我攻坚RN的原动力,就是借假修真。平台化技术最终王者也许花落Flutter或者小程序(还有很多人在纠结到底哪家强,耽误了学习,其实这好比考清华还是考北大,Top2高校有那么难选么,真正难选的是Top3高校),但这不重要,我能举一,必能反三,这就是霸道。我旨在强化出一套王者无界的方法论,如何从零将RN技能练到比肩高阶Android的熟练度,并且同样适用于进击Flutter和小程序。

授业(懂算法)

现在市面上高水准的RN解析文章太少了(老外写的硬核文章居多),而且大多停留在理论层面,只给出算法理论和源码片段,难以深入微操,只能作者说啥就是啥,反正不明觉厉。也罢,自力更生啃源码必须提上日程。

我始终相信,只有源码才是唯一的真相,不二的注释,思想的火花,王者的农药。

事实上,终于在眼泪中明白并验证了这一点,源码大法好啊,得到的比想要的多得多(贫穷限制了我的想象)。往小的说,技术成长(自嗨)。往大的说,核心竞争力(钱)。

本文和你分享的是如何通过先进生产力相对轻松地看懂代码,区别于呆板的流水式英文阅读,挑战一下:

  1. 承上(用户态–上层API怎么用)
    • 组件中方法(constructor、setState、forceUpdate、render)的作用是什么?
    • 生命周期调用时机是什么?
    • 子组件变化,父组件和兄弟组件是否会刷新?state和props变化,有什么不一样?
    • PureComponent比Component好在哪里,怎么能做得更好?
    • 最佳实践(JSX不创建临时函数,Immutable,性能优化)?
  2. 启下(内核态–底层原理怎么玩)
    • 各种概念的含义,对应数据结构是什么?
    • 深入浅出Fiber双树算法?
    • Diff算法在哪?
    • Native操作指令从哪来?

解惑(考考你)

聪明的童靴往往都会有一些亟需亲自操刀的疑问,我也不能免俗。问题有了,那满意的答案呢?

组件

  1. 明明只写了几个组件,通过react-devtools看到的却是一堆布局,而且还有Context.Consumer,这些啥时候冒出来的,干啥的?
  2. React组件和Native View看起来不是一一对应的,那么映射关系是什么?
  3. 组件普通API调用时机、作用和最佳实践?
// 组件类
class Component<P, S> {
	// 变量
	props;
	state;
	// 方法
	constructor(props, context);
	setState(state, callback): void;
	forceUpdate(callBack): void;
	render(): ReactNode;
}

生命周期

  1. 区分哪些方法只会调用一次,哪些可能会调用多次?哪些方法中能使用setState,哪些不能?
  2. 区分每个方法调用条件,是props改变还是state,是初始化,更新还是都有?
  3. React16.3开始废弃和新增的方法是哪些,补位策略是什么?废弃方法现在还能不能用,新旧方法混用又怎样?
  4. 组件生命周期API调用时机、作用和最佳实践?
// 静态生命周期
interface StaticLifecycle {
    getDerivedStateFromProps?: GetDerivedStateFromProps;
}
// 新生命周期
interface NewLifecycle<P, S, SS> {
    getSnapshotBeforeUpdate?(prevProps, prevState): SS | null;
    componentDidUpdate?(prevProps, prevState, snapshot): void;
}
// 废弃生命周期
interface DeprecatedLifecycle<P, S> {
    componentWillMount?(): void;
    UNSAFE_componentWillMount?(): void;
    componentWillReceiveProps?(nextProps, nextContext): void;
    UNSAFE_componentWillReceiveProps?(nextProps, nextContext): void;
    componentWillUpdate?(nextProps, nextState, nextContext): void;
    UNSAFE_componentWillUpdate?(nextProps, nextState, nextContext): void;
}
// 组件生命周期(继承新和废弃生命周期)
interface ComponentLifecycle<P, S, SS> extends NewLifecycle<P, S, SS>, DeprecatedLifecycle<P, S> {
    componentDidMount?(): void;
    shouldComponentUpdate?(nextProps, nextState, nextContext): boolean;
    componentWillUnmount?(): void;
    componentDidCatch?(error, errorInfo): void;
}

数据结构

  1. 区分Element、Instance、DOM、Component、Fiber的不同含义以及之间关系?
  2. Fiber节点数据结构中各属性含义?

Virtual DOM

  1. Virtual DOM遇到了哪些假问题,又解决了哪些真问题?
  2. React有棵DOM树,树在哪,怎么看,怎么操作对应Native View树?

Diff算法

  1. Diff算法的策略是什么,能得出哪些最佳实践?
  2. 都说React有个Diff算法(Tree Diff 分层求异;Component Diff 同类同树,异类异树;Element Diff 增删移复用key),代码在哪里,怎么比较的?

原理

  1. React高效在哪?怎么做到的?
  2. React工作流程?
  3. 如何关联Native自定义组件?
  4. Fiber双树是啥?凭什么这么牛?

追过程

学习

我们不是一个人在战斗(想发财),切忌闭门造车,只有集思广益,站在巨人的肩膀上才能事半功倍。

网上一顿关键字索引,找点时间,给点耐心,泛读 + 精读数十篇后,你的感觉才能慢慢滴上来。

本着坚定看多,数量堆死力量,经过不间断的阅读输出,姿势见涨,比方说通过XMind自由缩放源码地图帮助理解、手写RN寻求理论加实践、抽象伪代码表述助力说清楚等。

硬核带货时间,安利一下我的博客主页微信朋友圈,我会阶段性将看到的ReactNative优秀文章汇总起来。发盆友圈,我是认真的,停是不可能停下来的,天天上班天天发。欢迎相互切磋,共同进步。

Fiber架构里程碑

**Why:**一路狂奔式地更新,无暇处理用户响应,引发界面咔咔咔。

**What:**Fiber(纤维),是比线程控制更精密的并发处理机制。支持更新过程碎片化,化整为零,允许紧急任务插队,可中断恢复。本质上,它还是一个工具,用来帮助开发者操纵DOM API,从而构建出页面。

**How Much:**纵享丝滑。

**硬核资料:**业界大牛Lin Clark在2017 React大会的演讲Lin Clark - A Cartoon Intro to Fiber - React Conf 2017。这个内容太棒啦,墙裂建议大家看一看(没有字幕,英文流利的同学可以挑战一下,或者像我一样发挥暴躁的想象力假装听懂了)。网上大部分Fiber算法分析都引用了她的卡通图

术语

Component:组件,是可复用的小的代码片段,它们返回要在页面中渲染的React元素。分为类组件(继承Component的普通组件和继承PureComponent的纯组件)和函数式组件(直接返回Element的函数)。

// 普通组件
class App extends React.Component {
    render() {
        return <Text style={
  {color: 'black'}}>{'点击数0'}</Text>;
    }
}
// 纯组件
class App extends React.PureComponent {
    render() {
        return <Text style={
  {color: 'black'}}>{'点击数0'}</Text>;
    }
}
// 函数式组件
const App = function () {
    return <Text style={
  {color: 'black'}}>{'点击数0'}</Text>;
}

JSX:是类Html标签式写法转化为纯对象Element函数调用式写法的语法糖。Babel会把JSX转译成一个名为 React.createElement 函数调用.

class App extends React.Component {
	render() {
		// Babel转换JSX后
		return React.createElement(
			// 类型type
			{$$typeof: Symbol(react.forward_ref), displayName: "Text", propTypes: {…}, render: ƒ},
			// 属性props
			{style: {color: "black"}, __source: {…}},
			// 子节点children
			"点击数0"
		);
	}
}

Instance:组件实例,组件类实例化的结果,ref指向组件实例(函数式组件不能实例化)。在生成Fiber节点时会调用new Component()创建。

// App
{
	forceUpdate: ƒ (),
	isReactComponent: ƒ (),
	setState: ƒ (),
	componentDidMount: ƒ (),
	componentWillUnmount: ƒ (),
	constructor: ƒ App(props),
	isMounted: (...),
	render: ƒ (),
	replaceState: (...),
	__proto__: Component
}

Element:元素,描述了你在屏幕上想看到的内容。是DOM节点的一种纯对象描述,即虚拟DOM,对应组件render方法主要返回值。详见React.createElement

// App
{
	// React Element唯一标识
	$$typeof: Symbol(react.element),
	// 开发者指定唯一标识,用于复用
	key: null,
	// 属性
	props: {rootTag: 241},
	// 引用
	ref: null,
	// 类型
	type: ƒ App(props),
	_owner: null,
	_store: {validated: true},
	_self: null,
	_source: {fileName: "/Users/shengshuqiang/dream/AdvanceOnReactNative/Aw…native/Libraries/ReactNative/renderApplication.js", lineNumber: 38},
	__proto__: Object
}
// Text
{
	$$typeof: Symbol(react.element),
	key: null,
	props: {style: {color: "black"}, children: "点击数0"},
	ref: null,
	type: {$$typeof: Symbol(react.forward_ref), displayName: "Text", propTypes: {…}, render: ƒ},
	_owner: FiberNode {id: 11, tag: 1, key: null, elementType: ƒ, type: ƒ, …},
	_store: {validated: false},
	_self: null,
	_source: {fileName: "/Users/shengshuqiang/dream/AdvanceOnReactNative/AwesomeProject/App.js", lineNumber: 213},
	__proto__: Object
}

FiberNode:碎片化更新中可操作的细粒度节点,用于存储中间态计算结果,为“可紧急插队、可中断恢复”的页面刷新提供技术支持。详见ReactNative.createFiberFromElement

// FiberNode
{
	actualDuration: 175.7499999985157,
	actualStartTime: 9793.884999999136,
	// 候补树,在调用render或setState后,会克隆出一个镜像fiber,diff产生出的变化会标记在镜像fiber上。而alternate就是链接当前fiber tree和镜像fiber tree, 用于断点恢复
	alternate: null,
	// 第一个子节点
	child: FiberNode {id: 12, tag: 11, key: null, elementType: {…}, type: {…}, …},
	// TODO
	childExpirationTime: 0,
	contextDependencies: null,
	// 副作用,增删改操作。Placement=2;Update=4;PlacementAndUpdate=6;Deletion=8;
	effectTag: 5,
	// 描述了它对应的组件。对于复合组件,类型是函数或类组件本身。对于宿主组件(div,span等),类型是字符串。定义此 Fiber 节点的函数或类。对于类组件,它指向构造函数,对于 DOM 元素,它指定 HTML 标记。我经常使用这个字段来理解 Fiber 节点与哪个元素相关。
	// ClassComponent对应为函数,如APPContainer()。ForwardRef、ContextConsumer、ContextProvider对应为对象,如{$$typeof: Symbol(react.forward_ref), render: ƒ, displayName: "View"}。HostComponent对应为字符串,如“RCTView”。HostText对应为null。
	elementType: ƒ App(props),
	// TODO
	expirationTime: 0,
	// 用来保存中断前后 effect 的状态,用户中断后恢复之前的操作。这个意思还是很迷糊的,因为 Fiber 使用了可中断的架构
	firstEffect: FiberNode {id: 13, tag: 1, key: null, elementType: ƒ, type: ƒ, …},
	// 我添加的Fiber节点唯一标识(采用id自增生成),用于生成Fiber双树
	id: 11,
	index: 0,
	// 复用标识
	key: null,
	// 参考firstEffect
	lastEffect: FiberNode {id: 13, tag: 1, key: null, elementType: ƒ, type: ƒ, …},
	// 在前一个渲染中用于创建输出的 Fiber 的 props
	memoizedProps: {rootTag: 191},
	// 用于创建输出的 Fiber 状态。处理更新时,它会反映当前在屏幕上呈现的状态
	memoizedState: null,
	mode: 4,
	// workInProgress tree上每个节点都有一个effect list,用来存放需要更新的内容。此节点更新完毕会向子节点或邻近节点合并 effect list
	nextEffect: FiberNode {id: 10, tag: 5, key: null, elementType: "RCTView", type: "RCTView", …},
	// props是函数的参数。一个 fiber 的pendingProps在执行开始时设置,并在结束时设置memoizedProps。已从 React 元素中的新数据更新并且需要应用于子组件或 DOM 元素的 props
	pendingProps: {rootTag: 191},
	ref: null,
	// 父节点
	return: FiberNode {id: 10, tag: 5, key: null, elementType: "RCTView", type: "RCTView", …},
	selfBaseDuration: 28.63000000070315,
	// 兄弟节点
	sibling: null,
	// 保存组件的类实例、DOM 节点或与 Fiber 节点关联的其他 React 元素类型的引用。总的来说,我们可以认为该属性用于保持与一个 Fiber 节点相关联的局部状态。(HostRoot对应{containerInfo};ClassComponent对应为new的函数对象实例;HostComponent对应为ReactNativeFiberHostComponent,包含_children和_nativeTag;HostText对应为nativeTag)
	stateNode: hookClazz {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …},
	// 它在协调算法中用于确定需要完成的工作。如前所述,工作取决于React元素的类型
	tag: 1,
	treeBaseDuration: 155.36499999871012,
	// 同elementType
	type: ƒ App(props),
	// state更新队列。状态更新、回调和 DOM 更新的队列
	updateQueue: null,
	_debugID: 12,
	_debugIsCurrentlyTiming: false,
	_debugOwner: null,
	_debugSource: {fileName: "/Users/shengshuqiang/dream/AdvanceOnReactNative/Aw…native/Libraries/ReactNative/renderApplication.js", lineNumber: 38},
	__proto__: Object
}

DOM:文档对象模型(Document Object Model),简单说就是界面控件树(对应Html是DOM树,对应Native是View树)的节点。

UIManager.createView	[3,"RCTRawText",1,{"text":"点击数0"}]
UIManager.createView	[5,"RCTText",1,{"ellipsizeMode":"tail","allowFontScaling":true,"accessible":true,"color":-16777216}]
UIManager.setChildren	[5,[3]]
UIManager.createView	[7,"RCTView",1,{"flex":1,"pointerEvents":"box-none","collapsable":true}]
UIManager.setChildren	[7,[5]]
UIManager.createView	[9,"RCTView",1,{"pointerEvents":"box-none","flex":1}]
UIManager.setChildren	[9,[7]]
UIMana
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
当你初学Python时,可能会感到有些困惑和不知所措,不知道如何入门。在这篇文章中,我们将介绍一些Python小白必看的内容,帮助你快速入门。 1. 安装Python 首先,你需要安装Python。你可以在Python官方网站上下载最新版本的Python。安装过程很简单,只需按照提示进行操作即可。 2. 学习Python基础知识 了解Python的基础知识是入门的第一步。Python是一种高级编程语言,它简单易学,但功能强大。在学习Python时,你需要掌握以下几个方面: - 变量和数据类型 - 控制流程语句(if/else语句,for/while循环) - 函数和模块 - 面向对象编程(类和对象) 3. 编写Python程序 学习Python基础知识之后,你需要开始编写Python程序。在编写程序时,你可以使用Python集成开发环境(IDE),如PyCharm、Spyder、IDLE等。这些IDE都提供了代码提示、调试和其他功能,可以帮助你更轻松地编写Python程序。 4. 学习Python标准库 Python标准库包含了许多有用的模块和函数,可以帮助你更轻松地开发Python应用。一些常用的标准库模块包括: - math:数学函数 - os:操作系统接口 - sys:Python解释器相关的变量和函数 - datetime:日期和时间处理 - random:生成随机数 5. 学习Python第三方库 Python第三方库是由其他人开发的Python模块和程序包,可以扩展Python的功能。一些常用的第三方库包括: - NumPy:用于科学计算的库 - Pandas:用于数据分析的库 - Matplotlib:用于绘制图表的库 - Flask:用于Web开发的库 - TensorFlow:用于机器学习和深度学习的库 6. 学习Python编程实践 最后,学习编程实践非常重要。你需要通过编写实际的Python程序来加深对Python的理解。你可以参考一些Python开源项目,如Django、Requests、Scrapy等,来学习如何编写高质量的Python代码。 总之,学习Python需要不断地练习和实践。希望这篇文章能帮助你快速入门Python,并成为一名Python程序员。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值