Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

深入JSX #29

Open
isNeilLin opened this issue Jun 18, 2021 · 0 comments
Open

深入JSX #29

isNeilLin opened this issue Jun 18, 2021 · 0 comments
Labels
框架&工具库 Vue和React等前端框架或工具库相关

Comments

@isNeilLin
Copy link
Owner

isNeilLin commented Jun 18, 2021

查看原文

JSX作为描述组件内容的数据结构,为JS赋予了更多视觉表现力。在React中我们大量使用他。在深入源码之前,有些疑问我们需要先解决:

  • JSXFiber节点是同一个东西么?
  • React ComponentReact Element是同一个东西么,他们和JSX有什么关系?

带着这些疑问,让我们开始这一节的学习。

#JSX简介

相信作为React的使用者,你已经接触过JSX。如果你还不了解他,可以看下官网对其的描述 (opens new window)

JSX在编译时会被Babel编译为React.createElement方法。

这也是为什么在每个使用JSX的JS文件中,你必须显式的声明

import React from 'react';

否则在运行时该模块内就会报未定义变量 React的错误。

注意

在React17中,已经不需要显式导入React了。详见介绍全新的 JSX 转换(opens new window)

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

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

// 编译前
<p>KaSong</p>
// 编译后
h("p", null, "KaSong");

#React.createElement(opens new window)

既然JSX会被编译为React.createElement,让我们看看他做了什么:

export function createElement(type, config, children) {
  let propName;

  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  if (config != null) {
    // 将 config 处理后赋值给 props
    // ...省略
  }

  const childrenLength = arguments.length - 2;
  // 处理 children,会被赋值给props.children
  // ...省略

  // 处理 defaultProps
  // ...省略

  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 标记这是个 React Element
    $$typeof: REACT_ELEMENT_TYPE,

    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
  };

  return element;
};

我们可以看到,React.createElement最终会调用ReactElement方法返回一个包含组件数据的对象,该对象有个参数$$typeof: REACT_ELEMENT_TYPE标记了该对象是个React Element

所以调用React.createElement返回的对象就是React Element么?

React提供了验证合法React Element的全局API React.isValidElement (opens new window),我们看下他的实现:

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

那么JSXReact Component的关系呢?

#React Component

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

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


function AppFunc() {
  return <p>KaSong</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 
}

值得注意的一点,由于

AppClass instanceof Function === true;
AppFunc instanceof Function === true;

所以无法通过引用类型区分ClassComponentFunctionComponentReact通过ClassComponent实例原型上的isReactComponent变量判断是否是ClassComponent

ClassComponent.prototype.isReactComponent = {};

#JSX与Fiber节点

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

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

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

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

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

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

#参考资料

@isNeilLin isNeilLin added the 框架&工具库 Vue和React等前端框架或工具库相关 label Jun 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
框架&工具库 Vue和React等前端框架或工具库相关
Projects
None yet
Development

No branches or pull requests

1 participant