# 一、JSX的本质

# 1.JSX的语法糖

  • 本质是React.createElement的语法糖
  • 所有的JSX最终都会被转成React.createElement的函数调用。
const message1 = <h2>Hello React</h2>
const message2 = React.createElement(标签,属性,子组件);

# 2.源码

目录:packages/react/index.js

判断环境:开发环境就会有更多的报错提示

环境

报错:

  • 你是否忘记导出的你的组件

环境1

# 3.createElement源码

# 判断环境部分

  • export function createElement(type, config, children)
  • 参数分别是tag类型(组件),属性,子组件

疑问:children是复数,但是这里只传一个参数,那么怎么传复数形式呢?

# 取出config属性部分

  • 这一步将config里面的属性值取出来。
  • 判断有没有key,ref,self,source这些属性,取出来保存在当前函数内部。
  • 然后再用for in 遍历将config里面的propName保存在函数内部的propName里面。

环境1

# 判断children部分

  • 孩子的长度为arguments.length - 2
  • 长度不同做不同的事,长度1的话props.children = children; 长度>1,创建数组,把每个child存入数组。props.children = childrenArray;

环境1

# 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取出来调用。

setState

# enqueueSetState里面的requestCurrentTimeForUpdate

  • 里面关键的一点就是在requestCurrentTimeForUpdate这个函数里面,同步异步就是根据该函数里面的上下文决定的,生命周期和React合成事件/setTimeout和原生DOM事件 这两个渲染上下文是不一样的。
  • 根据不同上下文,返回一个不同时间currentTime

setState

# enqueueSetState里面的computeExpirationForFiber

  • 计算过期时间,把currentTime传进去,再把fiber(当前组件实例)传进去。
  • 在computeExpirationForFiber函数内部把fiber的mode取出来,去判断返回的是同步还是批量处理
  • 批处理就是异步的意思

setState

# 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

setState

但是如果想要他进行多次累加,就可以把setState里面的参数换为函数。

# 五、SCU、Memo、Pure

父组件里面嵌套子组件,当父组件重新render时,里面所有组件都会进行重新渲染,这是非常没必要的,可以通过SCU(类特有)手动判断是否需要更新,Memo包裹函数组件,继承PureComponent去自动判断是否更新。

# SCU

SCU

# PureComponent

  • 继承这个组件时,他就会把isPureReactComponent = true
  • 那么SCU里面的浅层比较将会被调用

SCU

# 浅层比较函数

  • 里面比较的是新旧props和新旧state
  • pureComponent默认返回的是false,不更新(render不调用)
  • 只要浅层比较就行了,深层比较浪费性能。

SCU

最后更新于: 12/25/2020, 9:20:27 AM