源码学习记录
1. React Api
- Fiber: react 16 中引入,解决js单线程运行时,计算量太大导致的动画卡帧和交互卡顿的问题。
- Flow Type: react中用到的类型检测。
- jsx到javaScript的转变: createElement(type, config, children) (
react.js
文件) 接受三个参数,使用babel转变查看:babel
createElement需要注意几点:
- 标签转换是通过React的createElement方法实现,该方法接受三个参数:分别为dom类型,props属性,第三个参数及之后的都算作该标签的子节点。
- 原生标签显示为字符串,而自定义组件以大写字母开头,显示为变量。
- 其中createElement()方法中获取默认属性的方式: 如果定义了defaultProps,则会设置默认的props值。
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
- 该方法返回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
$$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,
};
return element;
}
其中需要注意 $$typeof
表示react的节点,通过createElement() 方法创建的都是react的节点。
- Component和PureComponent: (
ReactBaseClasses.js
文件)
PureComponent 继承自 Component,继承方法使用了很典型的寄生组合式。PureComponent通过prop和state的浅比较来实现shouldComponentUpdate,所以当PureComponent的组件中对象或数组的值改变单引用地址不变则不会更新数据。
Component.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
eg: 当state中数组arr的值为['1'] 可以通过下面的拓展运算符在PureComponent中得到更新
this.setState({
arr: [...arr, '2']
})
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
// 检查组件是否需要更新的一个判断
pureComponentPrototype.isPureReactComponent = true;
- render
- 在reactDOM.js 中render 返回 legacyRenderSubtreeIntoContainer() 函数
render(
element: React$Element<any>,
container: DOMContainer,
callback: ?Function,
) {
invariant(
isValidContainer(container),
'Target container is not a DOM element.',
);
return legacyRenderSubtreeIntoContainer(
null,
element,
container,
false, // 这个参数为 true 时表明了是服务端渲染, false 为客户端渲染
callback,
);
},
- legacyRenderSubtreeIntoContainer 中 一部分是没有 root 之前我们首先需要创建一个 root,第二部分是有 root 之后的渲染流程。
1⃣️ : 创建root节点
function legacyCreateRootFromDOMContainer(
container: DOMContainer,
forceHydrate: boolean,
): _ReactSyncRoot {
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// First clear any existing content.
if (!shouldHydrate) {
let warned = false;
let rootSibling;
while ((rootSibling = container.lastChild)) {
container.removeChild(rootSibling); // ** 容器内部不要含有任何的子节点。一是肯定会被移除掉,二来还要进行 DOM 操作,可能还会涉及到重绘回流等等。
}
}
// Legacy roots are not batched.
return new ReactSyncRoot(container, LegacyRoot, shouldHydrate); // 创建了一个 FiberRoot 对像
}
创建了一个 FiberRoot 对象,并挂载到了 _internalRoot 上
function ReactSyncRoot(
container: DOMContainer,
tag: RootTag,
hydrate: boolean,
) {
// Tag is either LegacyRoot or Concurrent Root
const root = createContainer(container, tag, hydrate);
this._internalRoot = root;
}
function createContainer(
containerInfo: Container,
tag: RootTag,
hydrate: boolean,
): OpaqueRoot {
return createFiberRoot(containerInfo, tag, hydrate);
}
**content: ** 多用于多层父子组件关系传递信息。
ConcurrentMode : ConcurrentMode会降低包裹的子组件渲染的优先级。
2. React中的创建更新
React中对比一个ClassComponent是否需要更新: 只有两个地方。一是看有没有shouldComponentUpdate方法,二就是PureComponent判断
react中比较多的数据结构使用二进制: 为了简化类型比较,以及类型复合的方向
3. React调度过程(Fiber Scheduler)
什么是Fiber?
每一个ReactElement对应一个Fiber对象,记录节点的各种状态,串联整个应用形成树结构。
什么是update?
用于记录组件状态的改变。