React源码系列(二):实现React.createElement和JSX

1、浏览器是怎么认识JSX的?

javascript 复制代码
function App () {
  return <div>
    hello world
    <span>jsx是什么</span>
  </div>
}

这是一个我们通过JSX语法写的一个App 组件,但是这并不是一个js代码,所以浏览器并不识别这段代码,也就无法在浏览器上渲染出来,所以就需要通过babel把它编译成浏览器识别的js代码

php 复制代码
var _reactJsxRuntime = require("react/jsx-runtime");
function App() {
  return /*#__PURE__*/ _reactJsxRuntime.jsxs("div", {
    children: [
      "hello world",
      /*#__PURE__*/ _reactJsxRuntime.jsx("span", {
        children: "jsx\u662F\u4EC0\u4E48"
      })
    ]
  });
}

由代码可以看出,JSX最终由babel编译成了一个_jsxs()_reactJsxRuntime.jsxs方法的调用,从而将组件代码转化为可以执行的js代码。

2、在react源码中是怎么处理JSX的?

在react源码中,暴露了jsx方法,这个方法接收3个参数,最终返回了一个ReactElement方法的调用

而这个ReactElement方法最终返回了一个element对象

所以在React中,jsx的本质上就是ReactElement方法调用的方式,创建出来一个与之对应的element对象,这个对象包含$$typeof type key ref props等属性。

3、JSX是什么?

JSX就是JavaScript的一种语法扩展,是描述UI的一种方法,最终会以React.createElement()或者是jsx()方法执行的方式展现,返回一个ReactElement对象,在react17版本之后如果不使用其他react API就可以不用引入React,因为react官方和babel官方进行了合作,可以直接通过react/jsx-runtime直接进行转换。

4、实现React.createElement方法

最终返回一个element对象,所以先写ReactElement方法,构造一个element对象,也就是React元素

typescript 复制代码
const supportSymbol = typeof Symbol === 'function' && Symbol.for // 判断当前宿主环境是否支持Symbol

export const REACT_ELEMENT_TYPE = supportSymbol ? Symbol.for('react.element') : 0xeac7


/**
 * ReactElement构造函数
 * @param type
 * @param key 组件的key
 * @param ref 组件的ref
 * @param props 组件的props
 */
const ReactElement = (type, key, ref, props) => {
  const elelment = {
    $$typeof:REACT_ELEMENT_TYPE, // 通过这个属性来指明当前对象是ReactElement结构对象
  	
    type,
    key,
    ref,
    props,

    __mark: 'Apollo', // 用于区分真实React中的ReactElement对象
  }
  return element
}

由源码知道,ReactElement方法是在jsx方法中调用

kotlin 复制代码
/**
 * 
 * @param type 元素类型
 * @param config 元素属性,包括key,不包括子元素children
 * @param maybeChildren 子元素children
 * @returns 返回一个ReactElement
 */
const jsx  =(type, config, ...maybeChildren) => {
  // reactElement 自身的属性
  let key = null
  let ref = null
  // 创建一个空对象props,用于存储属性
  const props = {}
// 遍历config对象,将ref、key这些ReactElement内部使用的属性提取出来,不应该被传递下去
  for(const propName in config){
    const val = config[propName]
    if(propName === 'key'){
      if(val !== undefinde){
      	key = '' + val
      }
      continue
    }
    if(propName === 'ref'){
      if (val !== undefined) {
        ref = val
      }
      continue
    }
    // 如果propName是config自己的属性 则赋值给props
    if({}.hasOwnProperty.call(config, propName)){
      props[propName] = val
    }

    const childrenLength = maybeChildren.length
    if(childrenLength){
      if(childrenLength === 1){
        props.children = maybeChildren[0]
      }else{
        props.children = maybeChildren
      }
    }
  }
  return ReactElement(type, key, ref, props)
}

5、实现JSX方法

React.createElement方法和jsx方法的区别这里只体现在第三个参数上

ini 复制代码
/**
 * 
 * @param type 元素类型
 * @param config 元素属性
 * @param maybeKey 可能的key值
 * @returns 返回一个ReactElement
 */
const jsx = (type: ElementType, config: any, maybeKey: any) => {
  // 初始化key和ref为空
  let key = null;
  let ref = null;

  // 创建一个空对象props,用于存储属性
  const props: Props = {};

  // 遍历config对象,将ref、key这些ReactElement内部使用的属性提取出来,不应该被传递下去
  for (const prop in config) {
    const val = config[prop];
    if (prop === "key") {
      continue;
    }
    if (prop === "ref") {
      if (val !== undefined) {
        ref = val;
      }
      continue;
    }
    // 一般使用{...props}将所有属性都传递下去,所以摘除ref、key属性外需要被保存到props中
    if ({}.hasOwnProperty.call(config, prop)) {
      props[prop] = val;
    }
  }

  // 将 maybeKey 添加到 key 中
  if (maybeKey !== undefined) {
    key = "" + maybeKey;
  }

  return ReactElement(type, key, ref, props);
};

6、jsx方法和createElement的区别

jsx函数和createElement函数都用于在React中创建虚拟DOM元素,但它们的语法和用法有所不同。jsx函数来自于React 17及更高版本中的新的JSX转换功能,称为"Runtime Automatic"。

1、语法和转换方式:jsx函数用于处理新的JSX转换方式,其语法更简洁。createElement函数用于处理传统的JSX转换方式

javascript 复制代码
const element = <div className="container">Hello, world!</div>;

// 使用createElement转换后的代码如下
const element = React.createElement(
  "div",
  { className: "container" },
  "Hello, world!"
);

// 使用jsx函数(自动运行时)转换后的代码如下:
import { jsx as _jsx } from "react/jsx-runtime";

const element = _jsx("div", { className: "container", children: "Hello, world!" });

2、子元素和key值处理:jsx函数将子元素作为属性(children)传递,而createElement函数将子元素作为额外的参数传递。同时子元素上的key值在jsx函数中也会以第三个参数的形式传递,而在createElement函数中,则是存在于config第二个参数中。

php 复制代码
// 在createElement函数中:
React.createElement("div", {className: "app", key: "appKey"}, "hello,app");


// 在jsx函数中:
import { jsx as _jsx } from "react/jsx-runtime";

_jsx("div", {className: "app", children: "hello,app"}, "appKey");

3、兼容性和版本:createElement函数在所有React版本中可用,而jsx函数仅在React 17及更高版本中提供。尽管React团队推荐使用新的JSX转换方式,但许多现有项目可能仍在使用createElement函数

相关推荐
我爱加班、、2 分钟前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao2 分钟前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架
杨超越luckly8 分钟前
HTML应用指南:利用GET请求获取中国500强企业名单,揭秘企业增长、分化与转型的新常态
前端·数据库·html·可视化·中国500强
hedley(●'◡'●)38 分钟前
基于cesium和vue的大疆司空模仿程序
前端·javascript·vue.js·python·typescript·无人机
qq5_81151751540 分钟前
web城乡居民基本医疗信息管理系统信息管理系统源码-SpringBoot后端+Vue前端+MySQL【可直接运行】
前端·vue.js·spring boot
百思可瑞教育41 分钟前
构建自己的Vue UI组件库:从设计到发布
前端·javascript·vue.js·ui·百思可瑞教育·北京百思教育
百锦再41 分钟前
Vue高阶知识:利用 defineModel 特性开发搜索组件组合
前端·vue.js·学习·flutter·typescript·前端框架
CappuccinoRose1 小时前
JavaScript 学习文档(二)
前端·javascript·学习·数据类型·运算符·箭头函数·变量声明
这儿有一堆花1 小时前
Vue 是什么:一套为「真实业务」而生的前端框架
前端·vue.js·前端框架
全栈前端老曹1 小时前
【MongoDB】深入研究副本集与高可用性——Replica Set 架构、故障转移、读写分离
前端·javascript·数据库·mongodb·架构·nosql·副本集