# 一、JSX的本质
# 1.JSX的语法糖
- 本质是React.createElement的语法糖
- 所有的JSX最终都会被转成React.createElement的函数调用。
const message1 = <h2>Hello React</h2>
const message2 = React.createElement(标签,属性,子组件);
# 2.源码
目录:packages/react/index.js
判断环境:开发环境就会有更多的报错提示
报错:
- 你是否忘记导出的你的组件
# 3.createElement源码
# 判断环境部分
- export function createElement(type, config, children)
- 参数分别是tag类型(组件),属性,子组件
疑问:children是复数,但是这里只传一个参数,那么怎么传复数形式呢?
# 取出config属性部分
- 这一步将config里面的属性值取出来。
- 判断有没有key,ref,self,source这些属性,取出来保存在当前函数内部。
- 然后再用for in 遍历将config里面的propName保存在函数内部的propName里面。
# 判断children部分
- 孩子的长度为arguments.length - 2
- 长度不同做不同的事,长度1的话props.children = children; 长度>1,创建数组,把每个child存入数组。props.children = childrenArray;
# type部分
# 返回部分
- 返回的是ReactElement对象,最终创建的是ReactElement对象。
- 因为React利用ReactElement对象组成了一个JavaScript的对象树。
- 这个对象树就是大名鼎鼎的 虚拟DOM
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
# 二、setState同步异步判断
说明:setState有些时候是同步的,有些时候是异步的。多次累加只会作用一次,内部都是怎么做的?
# 初始判断
- partialState要么是对象,要么是函数,要么是null,必须是其中一个。
- 不是三个其中一个就会报出警告
- 实际执行的是updater.enqueueSetState
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.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
# updater
# enqueueSetState
- 上面提到的updater又是什么?它是属于react-reconciler里面的。他会把更新内容渲染到真实DOM上。
- 每个类组件都有updater,对应的是classComponentUpdater,setState就是把里面的enqueueSetState取出来调用。
# enqueueSetState里面的requestCurrentTimeForUpdate
- 里面关键的一点就是在requestCurrentTimeForUpdate这个函数里面,同步异步就是根据该函数里面的上下文决定的,生命周期和React合成事件/setTimeout和原生DOM事件 这两个渲染上下文是不一样的。
- 根据不同上下文,返回一个不同时间currentTime
# enqueueSetState里面的computeExpirationForFiber
- 计算过期时间,把currentTime传进去,再把fiber(当前组件实例)传进去。
- 在computeExpirationForFiber函数内部把fiber的mode取出来,去判断返回的是同步还是批量处理
- 批处理就是异步的意思
# enqueueSetState里面的createUpdate
- 上面计算过期时间的返回值,作为参数传给createUpdate,生成update(最上面源码)
# 三、setState数据合成
在setState修改数据时,state里面有多个属性,但修改只需要传入被修改的属性即可,怎么做到的呢?
# processUpdateQueue里面的getStateFromUpdate函数
- 获取到更新的state放入新的state
// Process this update.
newState = getStateFromUpdate(
workInProgress,
queue,
update,
newState,
props,
instance,
);
该函数返回
return Object.assign({}, prevState, partialState);
原因是做了一个浅拷贝
# 四、setState本身合成
一个合成事件里面有多个setState时,都只会进行一次,因为源码内部,进行了do...while...循环,do...while里面把进行了多次的setState,getStateFromUpdate就是在这个循环里面执行,每一次都是将前一次的state和下一次的state进行合并。
因为prevState一定是每一次都是do...while循环之前的值,所以多次更新只会执行最后一次。
但是如果想要他进行多次累加,就可以把setState里面的参数换为函数。
# 五、SCU、Memo、Pure
父组件里面嵌套子组件,当父组件重新render时,里面所有组件都会进行重新渲染,这是非常没必要的,可以通过SCU(类特有)手动判断是否需要更新,Memo包裹函数组件,继承PureComponent去自动判断是否更新。
# SCU
# PureComponent
- 继承这个组件时,他就会把isPureReactComponent = true
- 那么SCU里面的浅层比较将会被调用
# 浅层比较函数
- 里面比较的是新旧props和新旧state
- pureComponent默认返回的是false,不更新(render不调用)
- 只要浅层比较就行了,深层比较浪费性能。