createElement, ReactElement与Component部分源码解析(五)

原创 Lin_Grady 教程 javascript/jQuery 89阅读 2019-05-15 11:22:31 举报

React系列

React简单模拟语法(一)
Jsx, 合成事件与Refs(二)
virtualdom diff算法实现分析(三)
从Mixin到HOC再到HOOKS(四)
createElement, ReactElement与Component部分源码解析(五)

前言

因为之前写过一些文章分别关于怎么模拟React语法,React基本知识和virtualdom diff实现思路,接下来就跟着React源码大概了解一下怎么一个过程,只是主逻辑代码,忽略部分开发环境的代码.
以下解析仅限于我当时理解,不一定准确.

JSX编译

还是用之前的例子

编译成

createElement

我们看下API的语法

创建并返回给定类型的新 React element 。

  • type: 既可以是一个标签名称字符串,也可以是一个 React component 类型(一个类或一个函数),或者一个React fragment 类型。
  • props: 各种属性值
  • children: 子元素

因为有babel会编译JSX,所以一般很少会直接调用这个方法.

然后我们进入找到对应的源码位置查看代码react/packages/react/src/ReactElement.js

代码还比较简单,可以看出就是传入参数之后它会帮你做些特殊处理然后导出给ReactElement方法使用,如果有部分代码还不知道是干嘛的话也不用担心,下面会有说到

代码来看是开发模式下限制了对应keyref的取值器,使用时会执行对应方法进行报错不让读取.

至此相关源码基本了解了

getOwnPropertyDescriptor

上面其中核心方法介绍是这个

方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

  • obj
    需要查找的目标对象
  • prop
    目标对象内属性名称
  • 返回值

    如果指定的属性存在于对象上,则返回其属性描述符对象(property descriptor),否则返回 undefined。

属性描述符对象(property descriptor)

该方法允许对一个属性的描述进行检索。在 Javascript 中, 属性 由一个字符串类型的“名字”(name)和一个“属性描述符”(property descriptor)对象构成。

一个属性描述符是一个记录,由下面属性当中的某些组成的:

value

该属性的值(仅针对数据属性描述符有效)

writable

当且仅当属性的值可以被改变时为true。(仅针对数据属性描述有效)

get

获取该属性的访问器函数(getter)。如果没有访问器, 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)

set

获取该属性的设置器函数(setter)。 如果没有设置器, 该值为undefined。(仅针对包含访问器或设置器的属性描述有效)

configurable

当且仅当指定对象的属性描述可以被改变或者属性可被删除时,为true。

enumerable

当且仅当指定对象的属性可以被枚举出时,为 true。

ReactElement

然后再看看ReactElement的源码

整段代码来看它是在开发环境下对字段作处理:

  • 创建React元素,设置对应属性值
  • 开发环境下
    • 创建_store属性并配置其validated的属性描述符对象,达到方便调试React元素的目的
    • 配置_self的属性描述符对象,selfsource只是DEV的属性
    • 配置_source的属性描述符对象,出于测试的目的,应该将在两个不同位置创建的两个元素视为相等的,因此我们将它从枚举中隐藏起来。
    • 冻结element及其props对象

主要目的是为提高测试环境下效率,将element的一些属性配置为不可枚举,进行遍历的时候跳过这些属性。

其中REACT_ELEMENT_TYPE是

用来标识这个对象是一个ReactElement对象,至此Jsx编译成ReactElement对象的相关源码大概知道了.

React组件创建

createClass

16.0.0以后已经废弃,可忽略

ES6类继承

从官方例子来看React.Component

从源码看这个类写法做了什么react/packages/react/src/ReactBaseClasses.js

结构比较简单,结合注释可以知道只是基本赋值,里面有个更新器后面再说,现在先记住是用来更新State就行了.

注释很长实际代码很短,大概意思就是

  1. 不保证this.state会立即更新,所以调用方法之后可能获取的旧数据
  2. 不保证this.state会同步运行,可能最终他们会批量组合执行,可以提供一个可选完成回调当更新之后再执行
  3. 回调会在未来某个点执行,可以拿到最新的入参(state, props, context),不同于this.XX,因为它会在shouldComponentUpdate之前接收新的props属性之后执行,此时还没赋值给this.

这是强制更新视图的方法

  1. 这应该只在确定我们不是在一个DOM事务中时调用
  2. 这应该在当你知道某些深层嵌套组件状态已变但是没有执行setState的时候调用
  3. 它不会执行shouldComponentUpdate但会执行componentWillUpdatecomponentDidUpdate生命周期

接着我们看源码 react/packages/shared/invariant.js做了什么

使用constant()断言程序假定为真的状态,仅用于开发,会从生产环境中剥离保证不受影响

接下来我们再看看 react/packages/react/src/ReactNoopUpdateQueue.js

里面提供了三个函数,分别是

  • enqueueForceUpdate: 强制更新队列包装器
  • enqueueReplaceState: 状态替换队列包装器
  • enqueueSetState: 状态更新队列包装器

实际里面都是调用同一个方法warnNoop,设置首参数都一样.

目测应该是给传入的React组件实例设置componentName和KEY, 在里面我们再次看到同一个方法warningWithoutStack,我们直接看看他究竟做了什么. react/packages/shared/warningWithoutStack.js

类似invariant但是只有不满足条件的时候才会打印出警告.这可以用于在关键路径中记录开发环境中的问题.生产环境下会移除日志代码保证正常逻辑.代码只是一些基本的条件设定和优雅降级代码.

还有一个类似的继承类PureComponent,可以用于组件进行浅对比决定是否需要更新

基本代码和Component相似,也继承自它的原型.但不继承其自身的属性方法.

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复