人生就是你全部选择的总和。那么,你今天要作什么?——阿尔贝·加缪node
四年,如人生小溪中的一洼清水,如历史长河中的一点水滴,而却就是这四年,我完成了从懵懂到成熟的蜕变。回首这四年,有过创业,有过生病,有过说不出的苦楚,也有过让我笑不间断的喜悦。react
那年的背包,依然在背着;那年的代码,依然还在用相似的逻辑实现着;一件好的东西总会让我爱不释手,react就是其中一个,从React.createClass到React.createElement到React.Component;从Mixin到class component到functional component;从flux到redux、mobx到hooks;每一次更进一步,每一次爱更深一筹。就在这个时间节点,我以为我做为一个禅意开发者,应该记念一下我这位老情人了。git
这一系列文章与视频讲解(微信公众号:《JavaScript全栈》)将深刻剖析React源码。github
为了保证源码一致,请阅读与本文及视频相同版本,可到github下载,地址:github.com/Walker-Leee…redux
解读安排以下react-native
好了,感慨发完,咱们来一块儿揭开React神秘面纱吧!api
早期作react开发的同窗应该都知道,最开始react和react-dom在同一个包,后来为了作平台移植性,将react与react-dom分拆,相信作过react-native的同窗都知道,咱们写react-native项目时,也是用的react,只是表现层用了react-native的组件与api。因此看react源码咱们先分析react对于api的定义。数组
我将react中的部分代码片断展现于此promise
import {Component, PureComponent} from './ReactBaseClasses'; import {createRef} from './ReactCreateRef'; import {forEach, map, count, toArray, only} from './ReactChildren'; import { createElement, createFactory, cloneElement, isValidElement, jsx, } from './ReactElement'; import {createContext} from './ReactContext'; import {lazy} from './ReactLazy'; import forwardRef from './forwardRef'; import memo from './memo'; import { useCallback, useContext, useEffect, useImperativeHandle, useDebugValue, useLayoutEffect, useMemo, useReducer, useRef, useState, useResponder, useTransition, useDeferredValue, } from './ReactHooks'; 复制代码
二者的区别在于,PureComponent多给了一个标识,经过该标识在ReactFiberClassComponent
中处理,决定是否进行shalloEqual。浏览器
if (ctor.prototype && ctor.prototype.isPureReactComponent) { return ( !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) ); } 复制代码
比较state和props的值,来判断是否须要更新。
相似改对比的地方还有一个,就是在 shouldComponentUpdate
。
更新后的ref用法,咱们能够看到React即将抛弃<div>123</div>
,之后只能使用如下两种方式使用ref。
class App extends React.Component{ constructor() { this.ref = React.createRef() } render() { return <div ref={this.ref} /> // 或者是 return <div ref={(node) => this.ref = node} /> } } 复制代码
用来解决组件封装时,ref
的传递问题,你们看过antd源码的应该知道,不少组件使用到了 forwardRef
。好比form组件中,@Form.create()
将form组件相关的props绑定到组件上,this.props.validate
该文件中包含api有:forEach, map, count, toArray, only
,这些方法都是对于reactChildren的处理。
咱们在使用react彷佛少见createElement方法,由于在咱们如今项目中大多用上了jsx,大多时候是babel帮咱们将jsx转换为createElement,React.createElement('h1', {id: 'title'}, 'hello world')
。
cloneElement顾名思义,拷贝已有元素。
函数组件中相似pureComponent的用法,浅比较函数式组件的props,肯定是否须要更新。
export default function memo<Props>( type: React$ElementType, compare?: (oldProps: Props, newProps: Props) => boolean, ) { return { ?typeof: REACT_MEMO_TYPE, type, compare: compare === undefined ? null : compare, }; } 复制代码
在react中,调用createElement方法,返回值为ReactElement
。
export function createElement(type, config, children) { // ... return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); } // 与createElement相比较,预先定义ReactElement的type值,并返回ReactElement export function createFactory(type) { const factory = createElement.bind(null, type); factory.type = type; return factory; } 复制代码
咱们再来看看ReactElement的定义
const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // This tag allows us to uniquely identify this as a React Element // 该参数指明React节点类型 ?typeof: REACT_ELEMENT_TYPE, // Built-in properties that belong on the element // 标识改ReactElement属于什么类型 type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. // 记录 _owner: owner, }; return element; }; 复制代码
咱们能够发现,ReactElement
只是一个用来记录节点相关信息的对象,在后续的操做中经过该对象中的这些属性值,执行不一样类型逻辑。同时,这些信息在不一样平台渲染时,提供了脱离平台的能力。
type BaseFiberRootProperties = {| // 挂载节点,在ReactDOM.render方法接收的第二个参数 containerInfo: any, // 在持久更新时用到该属性,换言之不支持增量更新平台,在react-dom中不涉及 pendingChildren: any, // 当前应用对应的Fiber,即Root Fiber current: Fiber, // 如下顺序表示优先级 // 1) 还没提交(committed)的任务 // 2) 还未提交的挂起任务 // 3) 未提交的可能被挂起的任务 // 在提交时被挂起最老和最新任务 earliestSuspendedTime: ExpirationTime, latestSuspendedTime: ExpirationTime, // The earliest and latest priority levels that are not known to be suspended. // 不肯定是否会挂起的最老和最新任务(全部任务初始化都是该状态) earliestPendingTime: ExpirationTime, latestPendingTime: ExpirationTime, // The latest priority level that was pinged by a resolved promise and can be retried. latestPingedTime: ExpirationTime, // 若是有抛出错误且此时没有更多更新,此时咱们将尝试在处理错误前同步从头渲染 // 在renderRoot出现没法处理的错误时,该值会被置为`true` didError: boolean, // 等待提交任务的`expirationTime`属性 pendingCommitExpirationTime: ExpirationTime, // 已经完成的任务的FiberRoot对象,若是你只有一个Root,那他永远只多是这个Root对应的Fiber,或者是null // 在commit阶段,只会处理这个值对应的任务 finishedWork: Fiber | null, // 在任务被挂起时,经过setTimeout设置的返回内容,用来下一次若是有新的任务挂起时清理还没触发的timeout timeoutHandle: TimeoutHandle | NoTimeout, // 顶层context对象,只有主动调用renderSubtreeIntoContainer时才会使用到 context: Object | null, pendingContext: Object | null, // 用来肯定在第一次渲染时,是否须要合并 hydrate: boolean, // 当前root对象上所剩余的过时时间 nextExpirationTimeToWorkOn: ExpirationTime, // 当前更新对应的过时时间 expirationTime: ExpirationTime, // List of top-level batches. This list indicates whether a commit should be // deferred. Also contains completion callbacks. // 顶层批处理任务,该变量指明一个commit是否应该被推迟处理,同时包含了完成处理后的回调 firstBatch: Batch | null, // root之间关联的链表结构 nextScheduledRoot: FiberRoot | null, |}; 复制代码
// Fiber对应一个须要被处理或者已经处理的组件,组件与Fiber能够是一对多关系 type Fiber = {| // 不一样的组件类型 tag: WorkTag, // ReactElement里面的key key: null | string, // ReactElement.type,咱们调用`createElement`的第一个参数 elementType: any, // The resolved function/class/ associated with this fiber. // 异步组件resolved以后返回的内容,通常是`function`或者`class`,表示函数或class type: any, // The local state associated with this fiber. // 跟当前Fiber相关本地状态(若在浏览器环境中,该值为DOM节点) stateNode: any, // 指向他在Fiber节点树中的`parent`,用来在处理完这个节点以后向上返回 return: Fiber | null, // 指向自身的第一个子节点 // 单链表树结构 child: Fiber | null, // 指向自身的兄弟节点 // 兄弟节点的return与之指向同一个父节点 sibling: Fiber | null, index: number, // ref属性 ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject, // 新的更新带来的props pendingProps: any, // 上次渲染完成后的props memoizedProps: any, // 队列,存放该Fiber对应的组件产生的Update updateQueue: UpdateQueue<any> | null, // 上一次渲染时的state memoizedState: any, // 列表,存放这个Fiber依赖的context firstContextDependency: ContextDependency<mixed> | null, // 用来描述当前Fiber和他子树的`Bitfield` // 共存的模式表示这个子树是否默认是异步渲染的 // Fiber被建立的时候他会继承父Fiber // 其余的标识也能够在建立的时候被设置 // 可是在建立以后不该该再被修改,特别是他的子Fiber建立以前 mode: TypeOfMode, // Effect // 用来记录Side Effect effectTag: SideEffectTag, // 单链表用来快速查找下一个side effect nextEffect: Fiber | null, // 子树中第一个side effect firstEffect: Fiber | null, // 子树中最后一个side effect lastEffect: Fiber | null, // 表明任务在将来的哪一个时间点应该被完成 // 不包括他的子树产生的任务 expirationTime: ExpirationTime, // 快速肯定子树中是否有不在等待的变化 childExpirationTime: ExpirationTime, // 在Fiber树更新的过程当中,每一个Fiber都会有一个跟其对应的Fiber,current <==> workInProgress //在渲染完成后,保存fiber alternate: Fiber | null, // 调试相关,收集每一个Fiber和子树渲染时间 actualDuration?: number, actualStartTime?: number, selfBaseDuration?: number, treeBaseDuration?: number, _debugID?: number, _debugSource?: Source | null, _debugOwner?: Fiber | null, _debugIsCurrentlyTiming?: boolean, |}; 复制代码
这三个文件主要定义了react中操做相关的类型,值得一提的是,react中类型的定义与组合很巧妙,若是同窗以前未使用过这种思路,能够在权限设计系统中试用该方法。
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ export type SideEffectTag = number; // Don't change these two values. They're used by React Dev Tools. export const NoEffect = /* */ 0b00000000000; export const PerformedWork = /* */ 0b00000000001; // You can change the rest (and add more). export const Placement = /* */ 0b00000000010; export const Update = /* */ 0b00000000100; export const PlacementAndUpdate = /* */ 0b00000000110; export const Deletion = /* */ 0b00000001000; export const ContentReset = /* */ 0b00000010000; export const Callback = /* */ 0b00000100000; export const DidCapture = /* */ 0b00001000000; export const Ref = /* */ 0b00010000000; export const Snapshot = /* */ 0b00100000000; // Update & Callback & Ref & Snapshot export const LifecycleEffectMask = /* */ 0b00110100100; // Union of all host effects export const HostEffectMask = /* */ 0b00111111111; export const Incomplete = /* */ 0b01000000000; export const ShouldCapture = /* */ 0b10000000000; 复制代码
export const FunctionComponent = 0; export const ClassComponent = 1; export const IndeterminateComponent = 2; // Before we know whether it is function or class export const HostRoot = 3; // Root of a host tree. Could be nested inside another node. export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer. export const HostComponent = 5; export const HostText = 6; export const Fragment = 7; export const Mode = 8; export const ContextConsumer = 9; export const ContextProvider = 10; export const ForwardRef = 11; export const Profiler = 12; export const SuspenseComponent = 13; export const MemoComponent = 14; export const SimpleMemoComponent = 15; export const LazyComponent = 16; export const IncompleteClassComponent = 17; 复制代码
/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ export type SideEffectTag = number; // Don't change these two values. They're used by React Dev Tools. export const NoEffect = /* */ 0b00000000000; export const PerformedWork = /* */ 0b00000000001; // You can change the rest (and add more). export const Placement = /* */ 0b00000000010; export const Update = /* */ 0b00000000100; export const PlacementAndUpdate = /* */ 0b00000000110; export const Deletion = /* */ 0b00000001000; export const ContentReset = /* */ 0b00000010000; export const Callback = /* */ 0b00000100000; export const DidCapture = /* */ 0b00001000000; export const Ref = /* */ 0b00010000000; export const Snapshot = /* */ 0b00100000000; // Update & Callback & Ref & Snapshot export const LifecycleEffectMask = /* */ 0b00110100100; // Union of all host effects export const HostEffectMask = /* */ 0b00111111111; export const Incomplete = /* */ 0b01000000000; export const ShouldCapture = /* */ 0b10000000000; 复制代码
export type Update<State> = { // 更新的过时时间 expirationTime: ExpirationTime, // 该tag标识更新类型 // UpdateState -> 0; // ReplaceState -> 1; // ForceUpdate -> 2; // CaptureUpdate -> 3; tag: 0 | 1 | 2 | 3, // 更新内容,如调用setState时接收的第一个参数 payload: any, // 对应的回调函数,调用setState或render时 callback: (() => mixed) | null, // 指向下一个更新 next: Update<State> | null, // 指向下一个side effect nextEffect: Update<State> | null, }; export type UpdateQueue<State> = { // 每次操做完更新后的state baseState: State, // 队首的Update firstUpdate: Update<State> | null, // 队尾的Update lastUpdate: Update<State> | null, firstCapturedUpdate: Update<State> | null, lastCapturedUpdate: Update<State> | null, firstEffect: Update<State> | null, lastEffect: Update<State> | null, firstCapturedEffect: Update<State> | null, lastCapturedEffect: Update<State> | null, }; 复制代码
数据结构中有一个结构——链表,不知能否记得链表的遍历?最多见链表的遍历使用递归实现,该api实现就是借助递归。咱们以forEach为例来看看代码片断实现。
function forEachChildren(children, forEachFunc, forEachContext) { if (children == null) { return children; } const traverseContext = getPooledTraverseContext( null, null, forEachFunc, forEachContext, ); traverseAllChildren(children, forEachSingleChild, traverseContext); releaseTraverseContext(traverseContext); } 复制代码
function traverseAllChildrenImpl( children, nameSoFar, callback, traverseContext, ) { const type = typeof children; if (type === 'undefined' || type === 'boolean') { // All of the above are perceived as null. children = null; } let invokeCallback = false; if (children === null) { invokeCallback = true; } else { switch (type) { case 'string': case 'number': invokeCallback = true; break; case 'object': switch (children.?typeof) { case REACT_ELEMENT_TYPE: case REACT_PORTAL_TYPE: invokeCallback = true; } } } if (invokeCallback) { callback( traverseContext, children, // If it's the only child, treat the name as if it was wrapped in an array // so that it's consistent if the number of children grows. nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar, ); return 1; } let child; let nextName; let subtreeCount = 0; // Count of children found in the current subtree. const nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR; if (Array.isArray(children)) { for (let i = 0; i < children.length; i++) { child = children[i]; nextName = nextNamePrefix + getComponentKey(child, i); subtreeCount += traverseAllChildrenImpl( child, nextName, callback, traverseContext, ); } } else { const iteratorFn = getIteratorFn(children); if (typeof iteratorFn === 'function') { const iterator = iteratorFn.call(children); let step; let ii = 0; while (!(step = iterator.next()).done) { child = step.value; nextName = nextNamePrefix + getComponentKey(child, ii++); subtreeCount += traverseAllChildrenImpl( child, nextName, callback, traverseContext, ); } } else if (type === 'object') { let addendum = ''; const childrenString = '' + children; invariant( false, 'Objects are not valid as a React child (found: %s).%s', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum, ); } } return subtreeCount; } function traverseAllChildren(children, callback, traverseContext) { if (children == null) { return 0; } return traverseAllChildrenImpl(children, '', callback, traverseContext); } 复制代码
const POOL_SIZE = 10; const traverseContextPool = []; function releaseTraverseContext(traverseContext) { traverseContext.result = null; traverseContext.keyPrefix = null; traverseContext.func = null; traverseContext.context = null; traverseContext.count = 0; if (traverseContextPool.length < POOL_SIZE) { traverseContextPool.push(traverseContext); } } 复制代码