深刻理解JSX

本文来自 React技术揭秘html

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

  • JSX和虚拟DOM是同一个东西么?
  • React ComponentReact Element是同一个东西么,他们和 JSX有什么关系?

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

JSX简介

相信做为React的使用者,你已经接触过JSX。若是你还不了解他,能够看下官网对其的描述github

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

JSX编译 Demo 外网Demo 内网Demobabel

这也是为何在每一个使用JSX的JS文件中,你必须显式的声明数据结构

import React from 'react';
复制代码

不然在运行时该模块内就会报未定义变量 React的错误。编辑器

你能够经过@babel/plugin-transform-react-jsx插件告诉Babel编译时须要将JSX编译为何函数的调用(默认为React.createElement)。函数

好比在preact这个类React库中,JSX会被编译为一个名为h的函数调用。post

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

React.createElement

既然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,咱们看下他的实现:

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/>); 复制代码

React Component 分类 Demo

咱们能够从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与虚拟DOM

从上面的内容咱们能够发现,JSX是一种描述当前组件内容的数据结构,他并不能描述组件schedulereconcilerender相关的信息。好比以下信息就不包括在JSX中:

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

这些内容都是包含在虚拟DOM中的。

因此,在组件mount时,Reconciler根据JSX描述的组件内容生成组件对应的虚拟DOM。在update时,ReconcilerJSX与虚拟DOM保存的数据对比,为对比后状态有变化的虚拟DOM打上标记。

参考资料

经过这篇文章在运行时修改React.createElement达到消除页面全部div元素的效果

如何干掉知乎的所有DIV

相关文章
相关标签/搜索