0 前言
本React系列文章是基于React最新的代码仓库进行分析,也许和老版本有些许不同,大佬们可酌情观看。
1 Component && Pure Component
Component和PureComponent作为所有学习React开发的人的入口,其对我们来说还是比较重要的,所以本篇文章选择从此处作为切入进行分析。
源码位于:ReactBaseClasses.js文件。
该文件更多的类似于抽象类的作用,对很多进行定义,然后提供给外部调用,真正的细节实现并不在此处。
1.1 Component
组件更新的基类装饰器,主要工作就是初始化参数。
具体代码如下:
function Component(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
this.updater = updater || ReactNoopUpdateQueue;
}
主要是对props,context,updater这三个参数进行初始化。
其中最重要的就是初始化装饰器,因为react所有的操作都在存在updater的前提下继续下去的。我们在常规新建的时候是不会传入updater参数的,所以一般都是初始化的默认的ReactNoopUpdateQueue。
refs默认设置为emptyObject的空对象。
1.2 PureComponent
唯一的区别就是在PureComponent的prototype上带有了类型标示,会判断isPureReactComponent。
pureComponentPrototype.isPureReactComponent = true;
TIPS:React中对比一个ClassComponent是否需要更新,只有两个地方。一是看有没有shouldComponentUpdate方法,二就是这里的PureComponent判断。
关于updater将在专门的文章中进行叙述。
2 React Context
相关代码如下所示,已经删除了dev有关的逻辑。
export function createContext<T>(
defaultValue: T,
calculateChangedBits: ?(a: T, b: T) => number,
): ReactContext<T> {
if (calculateChangedBits === undefined) {
calculateChangedBits = null;
}
const context: ReactContext<T> = {
$$typeof: REACT_CONTEXT_TYPE,
_calculateChangedBits: calculateChangedBits,
_currentValue: defaultValue,
_currentValue2: defaultValue,
_threadCount: 0,
Provider: (null: any),
Consumer: (null: any),
};
context.Provider = {
$$typeof: REACT_PROVIDER_TYPE,
_context: context,
};
context.Consumer = context;
return context;
}
_currentValue:作为支持多个并发渲染器的解决方法,我们将一些渲染器归类为主要渲染器,而将其他渲染器归类为次要渲染器。我们预计最多只有两个并发渲染器:Reaction Native(主要)和Fabric(次要);Reaction DOM(主要)和Reaction ART(次要)。辅助渲染器将其上下文值存储在单独的字段中。
_threadCount:用于记录当前渲染器中支持并发渲染器的数量。
Provider,Consumer:这些是循环调用过的。
typeof:因为JSON不支持Symbol类型。所以即使服务器存在用JSON作为文本返回安全漏洞,JSON里也不包含Symbol.for(′react.element′)。React会检测element.typeof,如果元素丢失或者无效,会拒绝处理该元素。特意用 Symbol.for() 的好处是 Symbols 通用于 iframes 和 workers 等环境中。因此无论在多奇怪的条件下,这方案也不会影响到应用不同部分传递可信的元素。同样,即使页面上有很多个 React 副本,它们也 「接受」 有效的 $$typeof值。
3 React.createRef
这是一个具有单个可变值的不可变对象。实现原理也超级简单。
export function createRef(): RefObject {
const refObject = {
current: null,
};
return refObject;
}
就创建一个refOject,然后return即可。
用法通过dom的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} />
}
}
4 createElement
4.1 总体概述
创建一个ReactElement元素的接口。
export function createElement(type, config, children) {}
type:表示要创建的节点的类型,
config:表示创建节点的一些属性,比如id,class之类的。
children:创建节点中包含的内容,这个children可以又是一个ReactElement。
4.2 分段解析
if (hasValidRef(config)) {
ref = config.ref;
// dev环境下的类型警告
if (__DEV__) {
warnIfStringRefCannotBeAutoConverted(config);
}
}
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
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
解析:
大概就是对传递过来的config对象进行解析。
如果config不为空就开始进行解析,首先解析的就是ref属性。
如果存在Key,同样把key绑定到要生成的dom上去。
将传递进来的这些config,赋值到对应的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;
}
传递过来的children对象可以存在多个参数,上述代码的意义就是将这些参数赋值到this.props.children上去。
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
如果传入的当前JSX.Element存在默认props,再把默认的props赋值到当前这个已经创建好的Element对象中去。
最后调用ReactElement完成元素创建。
5 cloneElement
cloneElement的步骤和createElement的步骤十分相似,但是也存在一些不同的。
比如说,在clone之前会对传入的element进行类型校验,如果不符合条件将会直接报出错误。
invariant(
!(element === null || element === undefined),
'React.cloneElement(...): The argument must be a React element, but you passed %s.',
element,
);
在创建元素之前首先会把原props进行复制操作。
const props = Object.assign({}, element.props);
下面的操作就几乎是相同的了,比如说解析config,解析默认的props,解析children。
6 createFactory && isValidElement
createFactory是用来创建专门用来创建某一类ReactElement的工厂的,我们几乎不会涉及到。
isValidElement顾名思义就是用来验证是否是一个ReactElement的,基本也用不太到。
7 ReactElement
前面做了那么多铺垫,其实最终的目的就是突出创建ReactElement的方法。
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE,
type: type,
key: key,
ref: ref,
props: props,
_owner: owner,
};
return element;
};
在前端一堆操作完成之后,我们此时只需要将已经准备好的参数传入进行创建即可。
其中$$typeof代表的该对象作为react element的唯一表示,如果没有就无法被react对象识别。
_owner:记录负责创建此元素的组件(这里比较重要,在后面会做进一步分析)。
type,key,ref,props等由createElement处理完成之后传入,具体加工过程可以看关于createElement的源码。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!