一、基础
1. React API
看一下 React 暴露出来的 API:
const React = {
Children: {
map,
forEach,
count,
toArray,
only,
},
createRef,
Component,
PureComponent,
createContext,
forwardRef,
lazy,
memo,
Fragment: REACT_FRAGMENT_TYPE,
StrictMode: REACT_STRICT_MODE_TYPE,
Suspense: REACT_SUSPENSE_TYPE,
createElement: __DEV__ ? createElementWithValidation : createElement,
cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
isValidElement: isValidElement,
version: ReactVersion,
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
};
Children
官方提供的一些用于处理 props.children 的方法
createRef
新的ref用法,React即将抛弃 <div ref="myDiv" />
这种 string ref 的用法,将来你只能使用两种方式来使用 ref
class App extends React.Component{
constructor() {
this.ref = React.createRef()
}
render() {
return <div ref={this.ref} />
// or
return <div ref={(node) => this.funRef = node} />
}
}
或者使用函数组件
const App = () => {
const inputEl = useRef(null)
return (
<input ref={inputEl} type="text" />
)
}
Component & PureComponent
这两个类基本相同,唯一的区别是PureComponent的原型上多了一个标识
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
return (
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
);
}
这是检查组件是否需要更新的一个判断,ctor 就是你声明的继承自 Component or PureComponent 的类,他会判断你是否继承自 PureComponent,如果是的话就 shallowEqual 比较 state 和 props
PureComponent优点
继承PureComponent
的组件,若state
和props
前后不改变,则不重新渲染。
Component优点
灵活性大,可以自由根据实际情况在shouldComponentUpdate
生命周期中进行优化
createElement & cloneElement & createFactory & isValidElement
createElement
它可谓是 React 中最重要的 API 了,他是用来创建 ReactElement 的,但是多数人却从没见过也没用过,这是为啥呢?因为你用了 JSX,JSX 并不是标准的 js,所以要经过编译才能变成可运行的 js,而编译之后,createElement 就出现了:
// jsx
<div id="app">content</div>
// js
React.createElement('div', { id: 'app' }, 'content')
cloneElement
用于克隆一个 ReactElement
createFactory
是专门用来创建某一类 ReactElement 的工厂
isValidElement
用来验证是否是一个 ReactElement新的ref用法,React即将抛弃
2. React Element
该部分详细源码如下:
/**
* 创建并返回指定类型的 ReactElement
* @param {*} type - 可以是标签名字符串(如 'div' 或 'span'),也可以是 React 组件类型(class 组件或函数组件),或是 React fragment 类型
* @param {*} config - 配置项,标签的 attribute
* @param {*} children - 子元素
*/
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) {
// 单独处理 key 和 ref
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;
// Remaining properties are added to a new props object
// 存储除内置 props 外的其他 props
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
// 将一个或多个 children,即第三个及以后的参数,处理后赋值给 props.children
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;
}
// Resolve default props
// 若该类型存在默认的 props, 并且未进行配置,则按默认的进行赋值
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
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
// 用于标示 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;
};
3. React Component
class Welcome extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
componentDidMount(){}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
以上是我们使用类组件时的常见使用方式,主要使用 Component 和 PureComponent 这两个基类,该部分详细源码如下:
React.component
/**
* 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.
*/
import invariant from 'shared/invariant';
import lowPriorityWarning from 'shared/lowPriorityWarning';
import ReactNoopUpdateQueue from './ReactNoopUpdateQueue';
const emptyObject = {};
if (__DEV__) {
Object.freeze(emptyObject);
}
/**
* Base class helpers for the updating state of a component.
*/
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;
}
// 给 Components 赋值以做标记
Component.prototype.isReactComponent = {};
/**
* 给 Component 的原型增加 setState-更新状态 方法
* @param {object|function} partialState - 更新的状态值, 可以对象或函数,将和当前 state 合并,生成下一个状态
* @param {?function} callback - 回调函数,用于 state 更新完成后执行的回调函数
*/
Component.prototype.setState = function(partialState, callback) {
// 判断 partialState 是否符合条件
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.',
);
// 这里是 state 更新机制的重点
// 具体实现在 react-dom 中,不在 react
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
/**
* 给 Component 的原型增加 forceUpdate-强制更新 方法,用于强制更新场景,无论 props 和 state 是否改变
* @param {?function} callback - 回调函数,用于更新完成后执行的回调函数
*/
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};
重点:
- Component() 方法本质是一个类,多用于继承
- setState() 和 forceUpdate 是 Component 原型上的方法,其本质是调用 ReactNoopUpdateQueue.js 中的 enqueueSetState() 方法
- React.Component() 只涉及了props、context、refs、updater、isReactComponent、setState、forceUpdate,其他均没有自己实现
React.PureComponent
如果一个组件的渲染只依赖于外界传进去的 props 和自己的 state,而并不依赖于其他的外界的任何数据,也就是说像纯函数一样,给它什么,它就吐出(渲染)什么出来。这种组件的复用性是最强的。这里的 PureComponent 便是如此使用的,它只有在 props 或 state 改变时才会重新渲染 ```javascript // 通过借用构造函数,实现典型的寄生组合式继承,避免原型污染 function ComponentDummy() {} ComponentDummy.prototype = Component.prototype;
/**
- Convenience component with default shallow equality check for sCU. */ 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; // 无须再通过原型链再向上找一级. Object.assign(pureComponentPrototype, Component.prototype); pureComponentPrototype.isPureReactComponent = true;
export {Component, PureComponent};
```
重点:
1. 借助空方法 ComponentDummy 使用典型的寄生组合式继承方式,避免原型污染
2. 使用 Object.assign(pureComponentPrototype, Component.prototype) 复制prototype,便可以不用再去 Component 的原型中找方法,减少了一次原型链的查找
参考:
https://segmentfault.com/a/1190000019783986?utm_source=tag-newest
https://www.cnblogs.com/heyushuo/p/10057676.html