React源码浅析

知其然知其所以然,框架工的自我尝试进阶

React源码浅析

知识点:

React Api

createElementcreateContextJSX=>JSConcurrentModeRefComponentSuspenseHooks

React 中的更新创建

ReactDOM.renderFiberUpdateQueueFiberRootUpdateexpirationTime

Fiber Scheduler

scheduleWorkbatchedUpdatesperformWorkperformUnitOfWorkrequestWorkreact schedulerrenderRoot

开始更新

beginWork以及优化各类组件的更新过程调节子节点的过程

完成各个节点的更新

completeUnitOfWork虚拟DOM对比completeWork错误捕获处理unwindwork完成整棵树更新

提交更新

commitRoot整体流程开发时的帮助方法提交快照提交DOM插入提交DOM更新提交DOM删除提交所有生命周期

各功能的实现功能

context的实现过程ref的实现过程hydrate的实现过程React的事件体系

Suspense

更新优先级的概念更新挂起的概念Suspense组件更新timeout处理retry重新尝试渲染lazy组件更新

Hooks

核心原理useStateuseEffectuseContent其他Hooks API

React Api 相关

React.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const React = {
Children:{
map,
forEach,
count,
toArray,
only
},
createRef,
Component,
PureComponent,
createContext,
forWardRef,
lazy,
memo,
Fragment:REACT_FRAGMENT_TYPE,
StrictMode:REACT_STRICT_MODE_TYPE,
SuspenseLREACT_SUSPENSE_TYPE,
createElement:__DEV__?createElementWithValidation:createElement,
cloneElement:__DEV__?cloneElementWithValidation:cloneElement,
isValidElement:isValidElement,
version:ReactVersion,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED:ReactSharedInternals,
}
if (enableStableConcurrentModeAPIs) {
React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
React.Profiler = REACT_PROFILER_TYPE;
} else {
React.unstable_ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
React.unstable_Profiler = REACT_PROFILER_TYPE;
}

export default React;

ReactElement.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
// hasOwnProperty 方法返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是知否有指定的键)
const hasOwnProperty = Object.prototype.hasOwnProperty;
// 内建的props
const RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true,
};

let specialPropKeyWarningShown, specialPropRefWarningShown;
// 判断是否是合法的 ref
function hasValidRef(config) {
if (__DEV__) {
if (hasOwnProperty.call(config, 'ref')) {
/**
* getOwnPropertyDescriptor 返回指定对象上自有属性描述符(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
* get 方法。获取该属性的访问器函数(getter)。如果没有访问器,该值为 undefined
*/
const getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.ref !== undefined;
}
// 判断是否是合法的 key
function hasValidKey(config) {
if (__DEV__) {
if (hasOwnProperty.call(config, 'key')) {
const getter = Object.getOwnPropertyDescriptor(config, 'key').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.key !== undefined;
}
// 定义了 key 不合法抛出的警告
function defineKeyPropWarningGetter(props, displayName) {
const warnAboutAccessingKey = function() {
if (!specialPropKeyWarningShown) {
specialPropKeyWarningShown = true;
warningWithoutStack(
false,
'%s: `key` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName,
);
}
};
warnAboutAccessingKey.isReactWarning = true;
Object.defineProperty(props, 'key', {
get: warnAboutAccessingKey,
configurable: true,
});
}
// 定义了 ref 不合法抛出的警告
function defineRefPropWarningGetter(props, displayName) {
const warnAboutAccessingRef = function() {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
warningWithoutStack(
false,
'%s: `ref` is not a prop. Trying to access it will result ' +
'in `undefined` being returned. If you need to access the same ' +
'value within the child component, you should pass it as a different ' +
'prop. (https://fb.me/react-special-props)',
displayName,
);
}
};
warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, 'ref', {
get: warnAboutAccessingRef,
configurable: true,
});
}

/**
* Factory method to create a new React element. This no longer adheres to
* the class pattern, so do not use new to call it. Also, no instanceof check
* will work. Instead test $$typeof field against Symbol.for('react.element') to check
* if something is a React Element.
*
* @param {*} type
* @param {*} key
* @param {string|object} ref
* @param {*} self A *temporary* helper to detect places where `this` is
* different from the `owner` when React.createElement is called, so that we
* can warn. We want to get rid of owner and replace string `ref`s with arrow
* functions, and as long as `this` and owner are the same, there will be no
* change in behavior.
* @param {*} source An annotation object (added by a transpiler or otherwise)
* indicating filename, line number, and/or other information.
* @param {*} owner
* @param {*} props
* @internal
*/
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,

// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,

// Record the component responsible for creating this element.
_owner: owner,
};
// 开发环境的一些内容
if (__DEV__) {
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {};

// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
// self and source are DEV only properties.
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}

return element;
};

/**
* 创建并返回给定类型的新ReactElement。
* See https://reactjs.org/docs/react-api.html#createelement
*/
export function createElement(type, config, children) {
let propName;

// Reserved names are extracted
const props = {};

let key = null;
let ref = null;
let self = null;
let source = null;

if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = '' + config.key;
}

self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 如果不是内建的props以及本身拥有的属性,将剩余的属性传入到新的props对象中
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}

// 子对象可以是多个参数,并且这些参数会传输到新分配的props对象上。
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}

// 解析默认属性
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}

/**
* 返回一个生成给定类型的ReactElements的函数。(已废弃)
* See https://reactjs.org/docs/react-api.html#createfactory
*/
export function createFactory(type) {
const factory = createElement.bind(null, type);
// Expose the type on the factory and the prototype so that it can be
// easily accessed on elements. E.g. `<Foo />.type === Foo`.
// This should not be named `constructor` since this may not be the function
// that created the element, and it may not even be a constructor.
// Legacy hook: remove it
factory.type = type;
return factory;
}

export function cloneAndReplaceKey(oldElement, newKey) {
const newElement = ReactElement(
oldElement.type,
newKey,
oldElement.ref,
oldElement._self,
oldElement._source,
oldElement._owner,
oldElement.props,
);

return newElement;
}

/**
* 以 element 元素为样板克隆并返回新的 React 元素
* See https://reactjs.org/docs/react-api.html#cloneelement
*/
export function cloneElement(element, config, children) {
invariant(
!(element === null || element === undefined),
'React.cloneElement(...): The argument must be a React element, but you passed %s.',
element,
);

let propName;

// 复制原来的props
const props = Object.assign({}, element.props);

// 提取内建props相关的值
let key = element.key;
let ref = element.ref;
// Self is preserved since the owner is preserved.
const self = element._self;
// Source is preserved since cloneElement is unlikely to be targeted by a transpiler, and the original source is probably a better indicator of the true owner.
const source = element._source;

// Owner will be preserved, unless ref is overridden
let owner = element._owner;

if (config != null) {
if (hasValidRef(config)) {
// Silently steal the ref from the parent.
ref = config.ref;
owner = ReactCurrentOwner.current;
}
if (hasValidKey(config)) {
key = '' + config.key;
}

// Remaining properties override existing props
let defaultProps;
if (element.type && element.type.defaultProps) {
defaultProps = element.type.defaultProps;
}
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
if (config[propName] === undefined && defaultProps !== undefined) {
// Resolve default props
props[propName] = defaultProps[propName];
} else {
props[propName] = config[propName];
}
}
}
}

// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}

return ReactElement(element.type, key, ref, self, source, owner, props);
}

/**
* 验证对象是否为ReactElement。
* See https://reactjs.org/docs/react-api.html#isvalidelement
* @param {?object} object
* @return {boolean} True if `object` is a ReactElement.
* @final
*/
export function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}

ReactElement 通过 createElement 创建的,传入三个参数:

  • type(指代 ReactElement的类型)

    • 字符串。'div'等,被称为 HostComponent
    • Class。继承自 Component或者 PureComponent的组件,称为 ClassComponent
    • 方法。functinal Component
    • 原生。FragmentAsyncModelSymbol
  • config

  • children

ReactElement只是一个用来承载信息的容器,里面有几个信息:

  • type,用来判断如何创建节点
  • keyref这些特殊信息
  • props新的属性内容
  • $$type用于确定是否属于 ReactElement

ReactBaseClasses.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
/**
* Base class helpers for the updating state of a component.
*/
const emptyObject = {};
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the renderer.
this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};
// 核心方法
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
// DOM 实现的
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
// 强制更新
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;

function PureComponent(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
// isPureComponent 属性便于 REACT-DOM 后面辨认是否是纯组件
pureComponentPrototype.isPureReactComponent = true;

export {Component, PureComponent};

ReactCreateRef.js

1
2
3
4
5
6
export function createRef(){
const refObject = {
current:null,
}
return refObject;
}

Refs 提供了一种方式,允许我们访问 DOM 节点或者在 render 方法中创建的 React 元素。

使用 Refs 的情况:

  • 管理焦点,文本选择或者媒体播放
  • 触发强制动画
  • 集成第三方 DOM 库

使用 Refs 的方法:

  • string ref
  • function
  • createRef

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
export default class RefDemo extends React.Component {
constructor() {
super();
this.objRef = React.createRef();
// {current:null}
}

componentDidMount() {
setTimeout(() => {
this.refs.stringRef.textContent = 'string ref got'
this.methodRef.textContent = 'method ref got'
this.objRef.current.textContent = 'obj ref got'
}, 1000)
}

render() {
return (
<>
<p ref="stringRef">span1</p>
<p ref={ele => (this.methodRef = ele)}>span3</p>
<p ref={this.objRef}>span3</p>
</>
)
}
}

forwardRef.js

1
2
3
4
5
6
export default function forwardRef(render:(props,ref)){
return{
$$typeof:REACT_FORWARD_REF_TYPE,
render,
}
}

React.forwardRef 会创建一个 React 组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。

主要使用的是:

  • 转发 refs 到 DOM 组件
  • 在高阶组件中转发 refs

React.forwardRef 接受渲染函数作为参数。React 将使用 props 和 refs 作为参数来调用此函数。此函数应该返回 React 节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// PureComponent
const TargetComponent = React.forwardRef((props,ref)=>{
<input type="text" ref={ref} />
})

export default class Comp extends React.Component{
constructor(){
super();
this.ref = React.createRef();
}
componentDidMount(){
this.ref.current.value = 'ref get input';
}
render(){
return <TargetComponent ref={this.ref}/>
}
}

ReactContext.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export function createContext(defaultValue,calculateChangedBits){
if(calculateChangedBits === undefined){
calculateChangedBits = null;
}
const context = {
$$typeof:REACT_CONTEXT_TYPE,
_calculateChangedBits:calculateChangedBits,
_currentValue:defaultValue,
_currentValue2:defaultValue,
Provider:null,
Consumer:null
}

context.Provider = {
$$typeof:REACT_PROVIDER_TYPE,
_context:context
}
if(__DEV__){
...
}else{
context.Consumer = context;
}
return context;
}

ReactLazy.js

1
2
3
4
5
6
7
8
9
10
export function lazy(ctor:()=>Thenable<T,R>){
return{
$$typeof:REACT_LAZY_TYPE,
// 类 promise
_ctor:ctor,
_status:-1,
// 加载完返回的结果
_result:null
}
}

Hooks

简单用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, { useState,useEffect } from 'react';
export default ()=>{
const [name,setName] = useState('lbh');
useEffect(()=>{
console.log('component update')
// 解除绑定
return ()=>{
console.log('unbind')
}
},[])
return(
<>
<p>My name is:{name}</p>
<input type="text" value={name} onChange={e=>setName(e.target.value)}>
</>
)
}

ReactChildren.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
const SEPARATOP = '.';
const SUBSEPARATOR = ':';
function escape(key){
const escapeRegex = /[=:]/g;
const escaperLookup = {
'=':'=0',
':':'=2'
}
const escapedString = (''+key).replace(escapeRegex,function(match){
return escapeLookup[match];
})
return '$' + escapedString;
}

let didWarnAboutMaps = false;
const userProviderKeyEscapeRegex = /\/+/g;
function escapeUserProvidedKey(text){
return (''+text).replace(userProviderKeyEscapeRegex,'$&/');
}
// 对象池
const POOL_SIZE = 10;
const traverseContextPool = [];
function getPooledTraverseContext(mapResult,keyPrefix,mapFunction,mapContext){
if(traverseContextPool.length){
const traverseContext = traverseContextPool.pop();
traverseContext.result = mapResult;
traverseContext.keyPrefix = keyPrefix;
traverseContext.func = mapFunction;
traverseContext.context = mapContext;
traverseContext.count = 0;
return traverseContext;
}else{
return{
result:mapResult,
keyPrefix:keyPrefix,
func:mapFunction,
context:mapContext,
count:0
}
}
}
// 释放对象属性,少于指定长度时,将对象push 对象池
function releaseTravserContext(traverseContext){
traverseContext.result = null;
traverseContext.keyPrefix = null;
traverseContext.func = null;
traverseContext.context = null;
traverseContext.count = 0;
if(traverseContextPool.length < POOL_SIZE){
traverseContextPool.push(traverseContext)
}
}
// 核心
function traverseAllChildrenImpl(
children,
nameSoFar,
callback,
traverseContext,
) {
const type = typeof children;

if (type === 'undefined' || type === 'boolean') {
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,
nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar,
);
return 1;
}

let child;
let nextName;
let subtreeCount = 0;
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)
}

function getComponentKey(component,index){
if(
typeof component === 'object' &&
component !== null &&
component.key != null
){
return escape(component.key);
}
return index.toString(36);
}

function forEachSingleChild(bookKeeping,child,name){
const { func, context } = bookKeeping;
func.call(context,child,bookKeeping.count++);
}

function forEachChildren(children, forEachFunc, forEachContext) {
if (children == null) {
return children;
}
const traverseContext = getPooledTraverseContext(
null,
null,
forEachFunc,
forEachContext,
);
traverseAllChildren(children, forEachSingleChild, traverseContext);
releaseTraverseContext(traverseContext);
}

function mapSingleChildIntoContext(bookKeeping, child, childKey) {
const {result, keyPrefix, func, context} = bookKeeping;

let mappedChild = func.call(context, child, bookKeeping.count++);
if (Array.isArray(mappedChild)) {
mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);
} else if (mappedChild != null) {
if (isValidElement(mappedChild)) {
mappedChild = cloneAndReplaceKey(
mappedChild,
keyPrefix +
(mappedChild.key && (!child || child.key !== mappedChild.key)
? escapeUserProvidedKey(mappedChild.key) + '/'
: '') +
childKey,
);
}
result.push(mappedChild);
}
}

function mapIntoWithKeyPrefixInternal(children,array,prefix,func,context){
let escapedPrefix = '';
if(prefix != null){
escapedPrefix = escapeUserProvidedKey(prefix)+'/';
}
const traverseContext = getPooledTraverseContext(
array,
escapedPrefix,
func,
context
)
traverseAllChildren(children,mapSingleChildIntoContext,traverseContext);
releaseTraverseContext(traverseContext);
}

function mapChildren(children,func,context){
if(children == null){
return children;
}
const result = [];
mapIntoWithKeyPrefixInternal(children,result,null,func,context);
return result;
}

function countChildren(children){
return traverseAllChildren(children,()=>null,null);
}

function toArray(children){
const result = [];
mapIntoWithKeyPrefixInternal(children,result,null,child => child)
return result;
}

function onlyChild(children) {
invariant(
isValidElement(children),
'React.Children.only expected to receive a single React element child.',
);
return children;
}
export {
forEachChild as forEach,
mapChildren as map,
countChildren as count,
onlyChild as only,
toArray,
}

主要方法流程图:

graph TD
A(开始)
contextPool(contextPool)
mapIntoWithKeyPrefixInternal(mapIntoWithKeyPrefixInternal)
traverseAllChildren(traverseAllChildren)
traverseAllChildrenImpl(traverseAllChildrenImpl)
mapSingleChildrenContext(mapSingleChildrenContext)
是否多个节点{是否多个节点}
isArray{isArray}
循环每个节点((循环每个节点))
对每个节点调用mapFunc返回map之后的节点(对每个节点调用mapFunc返回map之后的节点)
往result中推入clone节点并替换key(往result中推入clone节点并替换key)
A --> mapIntoWithKeyPrefixInternal
mapIntoWithKeyPrefixInternal -. 开始的时候获取 .-> contextPool
mapIntoWithKeyPrefixInternal -. 结束之后归还 .-> contextPool
mapIntoWithKeyPrefixInternal --> traverseAllChildren
traverseAllChildren --> traverseAllChildrenImpl
traverseAllChildrenImpl --> 是否多个节点
是否多个节点 --Y--> 循环每个节点
是否多个节点 --N--> mapSingleChildrenContext
循环每个节点 --> traverseAllChildrenImpl
mapSingleChildrenContext --> 对每个节点调用mapFunc返回map之后的节点
对每个节点调用mapFunc返回map之后的节点 --> isArray
isArray --Y--> mapIntoWithKeyPrefixInternal
isArray --N--> 往result中推入clone节点并替换key

ReactContext.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export function createCOntext(defaultValue,calculateChangedBits){
if(calculateChangedBits === undefined){
calculateChangedBits = null;
}
const context = {
$$typeof:REACT_CONTEXT_TYPE,
_calculateChangeBits:calculateChangeBits,
_currentValue:defaultValue,
_currentValue2:defaultValue,
Provider:null,
Consumer:null
}
context.Provider = {
$$typeof:REACT_PROVIDER_TYPE,
_context:context,
}
context.Consumer = context;
return context;
}

memo.js

1
2
3
4
5
6
7
8
9
10
export default function memo(
type: React$ElementType,
compare?: (oldProps: Props, newProps: Props) => boolean,
){
return{
$$typeof:REACT_MEMO_TYPE,
type,
compare:compare === undefined ? null : compare,
}
}

创建更新的方式

主要是下面的知识点:

ReactDOM.renderReactDOM.rendersetStateforceUpdate

步骤:

  • 创建 ReactRoot
  • 创建 FiberRoot 和 RootFiber
  • 创建更新

ReactDOM.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// ReactDOM
const ReactDOM = {
createPortal,
findDOMNode(
componentOrElement,
){
if (componentOrElement == null) {
return null;
}
if (componentOrElement.nodeType === ELEMENT_NODE) {
return componentOrElement;
}
},
// REACTElememt,DOM包裹节点,回调方法
hydrate(element,container,callback){
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
// 协调更新的选项
true,
callback
)
},
render(element,container,callback){
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false,
callback
)
}
}

render 与 hydrate 方法都可以传入三个参数,包括 ReactElement、DOM包裹节点和渲染结束后执行的回调方法,返回 legacyRenderSubtreeIntoContainer 方法执行的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function legacyRenderSubtreeIntoContainer(
parentComponent,
children,
container,
forceHydrate,
callback
){
let root;
if(!root){
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(
container,
forceHydrate,
)
}
}

function legacyCreateRootFromDOMContainer(
container,
forceHydrate,
){
const shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// 没有ssr的情况
if(!shouldHydrate){
let rootSibling;
while((rootSibling = container.lastChild)){
// 删除 container 下面的所有子节点
container.removeChild(rootSibling)
}
}
const isConcurrent = false;
return new ReactRoot(container,isConcurrent,shouldHydrate)
}


function shouldHydrateDueToLegacyHeuristic(){
const rootElement = getReactRootElementInContainer(container);
return !!(
rootElement &&
// 普通element节点
rootElement.nodeType === ELEMENT_NODE &&
// data-reactroot 老版本ssr的节点
rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME)
)
}

function getReactRootElementInContainer(container){
if(!container){
return null;
}
// 判断节点
if(container.nodeType === DOCUMENT_NODE){
return container.documentElement;
}else{
return container.firstChild;
}
}

先判断 root 是否不存在,不存在调用legacyCreateRootFromDOMContainer,传入 container,forceHydrate两个参数。legacyCreateRootFromDOMContainer函数创建了一个 ReactRoot,而 forceHydrate布尔类型参数在 hydrate 传入 true,在 render 里面传入 false。前者是服务器渲染,后者是客户端渲染。服务器渲染当服务器呈现标记的节点,React 会保留它并附加事件处理程序,从而具有高性能的首屏加载,客户端渲染当初始DOM与当前 DOM 存在差异,则可能会更改节点。可以看到 如果是 falsecontainer.removeChild(rootSibling)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
function ReactRoot(
container,
isConcurrent,
hydrate
){
const root = DOMRender.createContainer(container,isConcurrent,hydrate);
this._internalRoot = root;
}
// react-reconciler/inline.dom
function createContainer(
containerInfo,
isConcurrent,
hydrate
){
// 创建 FiberRoot
return createFiberRoot(container,isConcurrent,hydrate)
}

// ReactFiberRoot
export function createFiberRoot(
containerInfo,
isConcurrent,
hydrate,
): FiberRoot {
//
const uninitializedFiber = createHostRootFiber(isConcurrent);

let root;
if (enableSchedulerTracing) {
root = {
current: uninitializedFiber,
containerInfo: containerInfo,
pendingChildren: null,

earliestPendingTime: NoWork,
latestPendingTime: NoWork,
earliestSuspendedTime: NoWork,
latestSuspendedTime: NoWork,
latestPingedTime: NoWork,

didError: false,

pendingCommitExpirationTime: NoWork,
finishedWork: null,
timeoutHandle: noTimeout,
context: null,
pendingContext: null,
hydrate,
nextExpirationTimeToWorkOn: NoWork,
expirationTime: NoWork,
firstBatch: null,
nextScheduledRoot: null,

interactionThreadID: unstable_getThreadID(),
memoizedInteractions: new Set(),
pendingInteractionMap: new Map(),
};
} else {
root = {
current: uninitializedFiber,
containerInfo: containerInfo,
pendingChildren: null,

earliestPendingTime: NoWork,
latestPendingTime: NoWork,
earliestSuspendedTime: NoWork,
latestSuspendedTime: NoWork,
latestPingedTime: NoWork,

didError: false,

pendingCommitExpirationTime: NoWork,
finishedWork: null,
timeoutHandle: noTimeout,
context: null,
pendingContext: null,
hydrate,
nextExpirationTimeToWorkOn: NoWork,
expirationTime: NoWork,
firstBatch: null,
nextScheduledRoot: null,
};
uninitializedFiber.stateNode = root;
return root;
}
0%