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函数

相关推荐
学习使我快乐0121 分钟前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio199522 分钟前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈1 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水2 小时前
简洁之道 - React Hook Form
前端
正小安4 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch6 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光6 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   6 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   6 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web6 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery