文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

「深入浅出」实现JSX的转换

2024-11-30 16:42

关注

前言

由于近期在看React框架源码、底层实现方面的知识,所以想把学习心得整理出来。

这也是一个新的系列「从0实现React 18核心模块」的第一篇。

接下来还会更新:render、commit阶段的实现,以及Hooks架构、useState、useEffect、单双节点Diff的过程还有React 18中的并发更新原理。

在看文章之前,我们可以先想几个问题:

下文提到的 big-react 是从0到1实现的React的核心功能模块原理的项目

如果自己实现一个 React 框架,它需要包含哪些内置的包:

如果还有一个必要的包,那就是react-dom:

react与react-reconciler包是什么

react包为我们提供了什么

当我们在项目中使用 React 构建界面时,主要使用的就是 react​ 包。它提供了开发者需要的所有API。如React.Component、React.createElement、React.useState等等,所以它也是大多数 React 项目的基础。

react-reconciler包实现了什么​

react-reconciler包是一个更底层、更高级的库,它实现了reconciliation协调算法,reconciliation是 React 的一种核心优化策略,用于在更新组件时比较虚拟DOM树的差异,并将实际更改应用到实际的DOM树。这有助于提高性能,因为避免了不必要的DOM操作。

它主要用于创建自定义渲染器,以及在不同的平台中去使用 React。例如,react-dom(用于Web平台)和react-native(用于移动应用)都使用react-reconciler作为底层库,实现了针对各自平台的渲染逻辑。

JSX 是什么

const element = <div className="container">Hello, world!div>;

在React中,JSX是一种JavaScript语法扩展,允许你在JavaScript代码中编写类似HTML的标记。要使用JSX,需要在构建过程中将其转换为标准的JavaScript代码。

通常,这个转换过程包括两个主要部分:

JSX 被 Babel 编译成了什么

在React 17之前,JSX语法会被编译成React.createElement函数的调用,用来创建虚拟DOM元素。

转换结果如下:

const element = React.createElement(
"div",
{ className: "container" },
"Hello, world!"
);

从React 17开始,引入了新的JSX转换功能,称为"Runtime Automatic"(自动运行时)。这意味着在使用JSX语法时,不再需要手动引入React库。在自动运行时模式下,JSX会被转换成新的入口函数,import {jsx as _jsx} from 'react/jsx-runtime'; 和 import {jsxs as _jsxs} from 'react/jsx-runtime';。

转换结果如下:

import { jsx as _jsx } from "react/jsx-runtime";

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

接下来我们就来实现jsx方法或React.createElement方法(包括dev、prod两个环境)。

工作量包括:

实现 jsx 转换方法

jsx 转换方法包括:

实现React.createElement

在React 17之前,JSX转换应用的是createElement方法,下面是它的实现:


const createElement = (
type: ElementType,
config: any,
...maybeChildren: any
) => {
// reactElement 自身的属性
let key: Key = null;
let ref: Ref = null;

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

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

const maybeChildrenLength = maybeChildren.length;
if (maybeChildrenLength) {
// [child] [child, child, child]
if (maybeChildrenLength === 1) {
props.children = maybeChildren[0];
} else {
props.children = maybeChildren;
}
}

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

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

实现jsx方法

从React 17之后,JSX转换应用的是jsx方法,下面是它的实现:


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);
};

这段代码定义了一个jsx函数,主要用于创建React元素。首先,它会提取可能存在的key和ref属性,并将剩余属性添加到一个新的props对象中。最后用ReactElement函数创建一个React元素并返回。

从上面代码中可以看到还实现了ReactElement方法:

// jsx-runtime.js
const supportSymbol = typeof Symbol === 'function' && Symbol.for;

// 为了不滥用 React.elemen,所以为它创建一个单独的键
// 为React.element元素创建一个 symbol 并放入到 symbol 注册表中
export const REACT_ELEMENT_TYPE = supportSymbol
? Symbol.for('react.element')
: 0xeac7;

export const ReactElement = function (type, key, ref, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE,
type,
key,
ref,
props,
_mark: 'lsh',
};
return element;
};

export const jsx =...

用自己实现的的jsx接入Demo

我们试着把自己实现的jsx方法,创建一个ReactElement,看它是否能够渲染在页面上。

实现jsx方法

jsx-Demo运行地址

jsx方法和createElement的区别

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

以下是两者之间的主要区别:

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

例如,一个JSX元素:

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!" });
  1. ​子元素和key值处理:jsx函数将子元素作为属性(children)传递,而createElement函数将子元素作为额外的参数传递。同时子元素上的key值在jsx函数中也会以第三个参数的形式传递,而在createElement函数中,则是存在于config第二个参数中。

在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");
  1. ​兼容性和版本:createElement函数在所有React版本中可用,而jsx函数仅在React 17及更高版本中提供。尽管React团队推荐使用新的JSX转换方式,但许多现有项目可能仍在使用createElement函数。

这时可能产生两个疑问:

  1. 简化组件代码:不再需要在每个组件文件顶部添加**import React from 'react';**。这使得组件代码更简洁,更易于阅读和维护。
  2. 节省包大小:由于不再需要导入整个React对象,构建工具可以更好地优化输出代码,从而减小输出包的大小。

在之前的React版本中,每当创建一个新的React元素时,React都需要从属性对象中提取key​和ref,这会导致额外的性能开销。

将key​作为单独的参数传递,可以让React在处理虚拟DOM树时更容易地访问key,无需每次都从属性对象中查找。这有助于提高React的性能和效率,特别是在处理大量元素和复杂组件树时。

实现打包流程

打包流程稍微有些复杂,后续写到文章里。

简单来说就是使用 Rollup,将编写jsx方法的文件打包出来,通过pnpm link --global的方式生成一个全局的react包,这样就可以通过pnpm link react --global调试自己创建的 create-react-app demo项目了。

构建react包思路

来源:前端时光屋内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯