871 字
4 分钟
React源码学习-jsx的理解-part4

jsx理解#

JSXFiber节点不是同一个东西 。

React Component 和 React Element 也不是一个东西。

从编译来看#

JSX在babel中会被编译成React.createElement(这也就是为什么需要手动import React from 'react'的原因了)

但是在17之后不需要手动引入了 看这篇

如下图:

image-20210822155213907

JSX并不是只能被编译为React.createElement方法,你可以通过@babel/plugin-transform-react-jsx (opens new window)插件显式告诉Babel编译时需要将JSX编译为什么函数的调用(默认为React.createElement)。

比如在preact (opens new window)这个类React库中,JSX会被编译为一个名为h的函数调用。

React.createElement#

React.createElement地址

createElement(type, config, children)
  • type: react component类型
  • config:react component 的一些属性
  • children:它的子孙react component

执行步骤#

  1. 进来createElement我们会发现它定义了一些字段,这些字段都是我们比较常用的

    image-20210822155830376

  2. 之后我们对传进来的config进行校验,我们会发现他做了几个合法性的校验,并且对相对应的变量进行赋值

    image-20210822160045714

  3. 遍历config中的属性,将除了保留属性之外的其他属性赋值给Props(就是内部的一个中间对象)

    保留属性有哪些呢?

    可以看到react把,ref,key都提出来了,单独的作为 ReactElement函数的参数传递(这个下面说)

  4. 接下来处理type中的defaultProps,这里也能明白,因为我们经常需要给class的组件的一些参数设置默认的属性值

    image-20210822160957936

  5. 接下来我们走入ReactElement函数

    可以发现,它最终返回了一个Element对象

    const ReactElement = function(type, key, ref, self, source, owner, props) {
    ......
    const element = {
    // 标记这是个 React Element
    $$typeof: REACT_ELEMENT_TYPE,
    // 这个是react component的类型
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
    };
    return element;
    };
这里要注意,其中` $$typeof`这个参数很重要,主要是用来[isValidElement](https://github.com/facebook/react/blob/1fb18e22ae66fdb1dc127347e169e73948778e5a/packages/react/src/ReactElement.js#L547)函数来判断这个element是不是合法的react element
```js
export function isValidElement(object) {
return (
typeof object === 'object' &&
object !== null &&
object.$$typeof === REACT_ELEMENT_TYPE
);
}

可以看到,$$typeof === REACT_ELEMENT_TYPE的非null对象就是一个合法的React Element。换言之,在React中,所有JSX在运行时的返回结果(即React.createElement()的返回值)都是React Element

React Component#

React中,我们常使用ClassComponentFunctionComponent构建组件。

class AppClass extends React.Component {
render() {
return <p>111</p>
}
}
console.log('这是ClassComponent:', AppClass);
console.log('这是Element:', <AppClass/>);
function AppFunc() {
return <p>222</p>;
}
console.log('这是FunctionComponent:', AppFunc);
console.log('这是Element:', <AppFunc/>);

我们可以从Demo控制台打印的对象看出,ClassComponent对应的Elementtype字段为AppClass自身。

FunctionComponent对应的Elementtype字段为AppFunc自身,如下所示:

{
$$typeof: Symbol(react.element),
key: null,
props: {},
ref: null,
type: ƒ AppFunc(),
_owner: null,
_store: {validated: false},
_self: null,
_source: null
}

JSX与Fiber节点#

从上面的内容我们可以发现,JSX是一种描述当前组件内容的数据结构,他不包含组件schedulereconcilerender所需的相关信息。

比如如下信息就不包括在JSX中:

  • 组件在更新中的优先级
  • 组件的state
  • 组件被打上的用于Renderer标记

这些内容都包含在Fiber节点中。

所以,在组件mount时,Reconciler根据JSX描述的组件内容生成组件对应的Fiber节点

update时,ReconcilerJSXFiber节点保存的数据对比,生成组件对应的Fiber节点,并根据对比结果为Fiber节点打上标记

React源码学习-jsx的理解-part4
https://nollieleo.github.io/posts/react源码学习-jsx的理解-part4/
作者
翁先森
发布于
2021-08-21
许可协议
CC BY-NC-SA 4.0