有了 React.createElement 为什么还需要 JSX runtime,作用是什么?

简介: 之前的一篇 基于 Webpack 从 0 到 1 启动一个 React 项目 文章中有介绍的是如何从 0 到 1 配置 React 项目中的 JSX 转换,在查阅文档时有介绍到从本质,JSX 只是为

之前的一篇 基于 Webpack 从 0 到 1 启动一个 React 项目 文章中有介绍的是如何从 01 配置 React 项目中的 JSX 转换,在查阅文档时有介绍到

从本质上讲,JSX 只是为  React.createElement(component, props, ...children) 函数提供的语法糖

但真正在浏览器查看编译结果时发现 JSX 没有完全和 React.createElement(component, props, ...children) 画等号,实际上编译结果有可能是两个

  1. React.createElement(component, props, ...children)
  2. JSX runtime

您可以在线查看完整的示例源代码

区别

首先从示例里面找到这两个编译结果,分别如下

// React.createElement
return /*#__PURE__*/ React.createElement(
  "div",
  null,
  "count:" + count,
  /*#__PURE__*/ React.createElement(
    "button",
    {
      onClick: handleClick,
    },
    "click + 1"
  )
);
// JSX runtime
return /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsxs)(
  "div",
  {
    children: [
      "count:" + count,
      /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx)(
        "button",
        {
          onClick: handleClick,
          children: "click + 1",
        }
      ),
    ],
  }
);

为了便于观看,我做了格式化处理,实际编译结果应该只有一行

阅读过后会发现只有渲染函数上有区别((0, fn)()IFFE),但是根据 React 文档上说 JSX 实际上是 React.createElement 的语法糖,那为什么还需要 react_jsx_runtime__WEBPACK_IMPORTED_MODULE_1__.jsx(其实就是 JSX runtime

JSX runtime

其实对于这个问题,React 文档专门有一个博客文章来介绍这个部分,JSX runtime 是一种新的 JSX 转换,像过去做 JSX 转换的时候,比如使用 @babel/preset-react 去转,默认是将 JSX 编译成 React.createElement 的形式,而现在你使用通过配置以后再转就是 JSX runtime 的形式,具体配置如下

// .babelrc
// React.createElement
{
  "presets": [
    ["@babel/preset-react", {
      "runtime": "classic"
    }]
  ]
}
// .babelrc
// JSX runtime
{
  "presets": [
    ["@babel/preset-react", {
      "runtime": "automatic"
    }]
  ]
}

对于 React 这种世界级流行的前端框架,换渲染函数(无论是 React.createElement 还是 JSX runtime 底层都一样的渲染逻辑,只不过名字换了而已)这种事情肯定有它自己的理由,为此它给出的推出的新转换的优势如下

优势

  • 使用全新的转换,你可以单独使用 JSX 而无需引入 React
  • 根据你的配置,JSX 的编译输出可能会略微改善 bundle 的大小
  • 它将减少你需要学习 React 概念的数量,以备未来之需。

说实话这三点里面一、三点都非常离谱,首先是

第三点我实在是想不出来有什么减少的,难道 JSX 编译成 React.createElement 的八股不会变成 JSX 编译成 JSX runtime 的八股吗?

无需引入 React

还有第一点,在博客里面的语境意思就是,我们不再需要去写组件的时候,写上很别扭的一行引用

// React 17 RC 以前
import React from "react";

function App() {
  return <h1>Hello World</h1>;
}
// React 17 RC 及其以后
function App() {
  return <h1>Hello World</h1>;
}

为什么需要 import React from "react"; 是因为它的编译结果是需要 React.createElement,虽然在源代码里面没有用到,但是编译后是需要的,否则你会收到一个报错

Uncaught ReferenceError: React is not defined

但也恰恰是因为源代码(编译前)没有用到,像一些编译器或者包会频繁给你提醒,提示存在一个未使用的引用,如果小白不懂,按提示操作就会喜提上面的报错

减小打包体积

根据你的配置,JSX 的编译输出可能会 略微改善 bundle 的大小

这一点我认为是比较重要的(可以考察候选人对模块的理解)

为什么改变新转换以后还可以减小打包体积?举个例子

// React.createElement
// 手动引入 react
import React from 'react';

function App() {
  return React.createElement('h1', null, 'Hello world');
}
// JSX runtime
// 由编译器引入(禁止自己引入!)
import {jsx as _jsx} from 'react/jsx-runtime';

function App() {
  return _jsx('h1', { children: 'Hello world' });
}

上面对于渲染函数的引用分为了两种方式

  1. _jsx 解构引用
  2. React.createElement 全量引用

直接引用 React 会将非 createElement 相关的 API (比如上面例子中就没有用到 useState)一并被打包进编译文件中,而使用解构,可以通过 tree-sharking 来排除掉项目中未使用到的 React API,这就是为什么 JSX runtime 可以减小打包体积

看到这里你应该明白了,其实 _jsx 可以理解为 createElement,如下

import { createElement } from 'react';

function App() {
  return createElement('h1', { children: 'Hello world' });
}

但因为在 React 17 RC 以前,编译结果只能是 React.createElement(...),因此无法修改成上面这种写法,但不知道出于什么原因 React 在改进的时候直接选择了换掉 React.createElement

Tree Sharking

解构就能减小打包体积的原因是什么呢?怎么减的?这里可以给出 rolluptree-sharking 编译结果的例子](https://rollupjs.org/repl/?version=3.7.5&shareable=JTdCJTIybW9kdWxlcyUyMiUzQSU1QiU3QiUyMm5hbWUlMjIlM0ElMjJtYWluLmpzJTIyJTJDJTIyY29kZSUyMiUzQSUyMiUyRiolMjBUUkVFLVNIQUtJTkclMjAqJTJGJTVDbmltcG9ydCUyME1hdGglMjBmcm9tJTIwJy4lMkZtYXRocy5qcyclM0IlNUNuJTVDbmNvbnNvbGUubG9nKCUyME1hdGguY3ViZSglMjA1JTIwKSUyMCklM0IlMjAlMkYlMkYlMjAxMjUlMjIlMkMlMjJpc0VudHJ5JTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMm5hbWUlMjIlM0ElMjJtYXRocy5qcyUyMiUyQyUyMmNvZGUlMjIlM0ElMjIlMkYlMkYlMjBtYXRocy5qcyU1Q24lNUNuZXhwb3J0JTIwZGVmYXVsdCUyMCU3QiU1Q24lMjAlMjBzcXVhcmUoeCklMjAlN0IlNUNuJTVDdCUyMCUyMHJldHVybiUyMHglMjAqJTIweCUzQiU1Q24lMjAlMjAlN0QlMkMlNUNuJTIwJTIwY3ViZSUyMCh4KSUyMCU3QiU1Q24lNUN0JTIwJTIwcmV0dXJuJTIweCUyMColMjB4JTIwKiUyMHglM0IlNUNuJTIwJTIwJTdEJTVDbiU3RCUyMiUyQyUyMmlzRW50cnklMjIlM0FmYWxzZSU3RCU1RCUyQyUyMm9wdGlvbnMlMjIlM0ElN0IlMjJmb3JtYXQlMjIlM0ElMjJlcyUyMiUyQyUyMm5hbWUlMjIlM0ElMjJteUJ1bmRsZSUyMiUyQyUyMmFtZCUyMiUzQSU3QiUyMmlkJTIyJTNBJTIyJTIyJTdEJTJDJTIyZ2xvYmFscyUyMiUzQSU3QiU3RCU3RCUyQyUyMmV4YW1wbGUlMjIlM0FudWxsJTdE))

输入:(React.createElement 形式)

// main.js
import Math from './maths.js';

console.log( Math.cube( 5 ) ); // 125
// maths.js

const square = function (x) {
    return x * x;
};

const cube = function (x) {
    return x * x * x;
}

export { square, cube };

export default {
  square,
  cube
}

输出:

// maths.js

const square = function (x) {
    return x * x;
};

const cube = function (x) {
    return x * x * x;
};

var Math = {
  square,
  cube
};

/* TREE-SHAKING */

console.log( Math.cube( 5 ) ); // 125

输入:(JSX runtime 形式)

// main.js
import { cube } from './maths.js';

console.log( Math.cube( 5 ) ); // 125
// maths.js

const square = function (x) {
    return x * x;
};

const cube = function (x) {
    return x * x * x;
}

export { square, cube };

export default {
  square,
  cube
}

输出:

// maths.js

const cube = function (x) {
    return x * x * x;
};

/* TREE-SHAKING */

console.log( cube( 5 ) ); // 125

在线查看完整代码](https://rollupjs.org/repl/?version=3.7.5&shareable=JTdCJTIybW9kdWxlcyUyMiUzQSU1QiU3QiUyMm5hbWUlMjIlM0ElMjJtYWluLmpzJTIyJTJDJTIyY29kZSUyMiUzQSUyMiUyRiolMjBUUkVFLVNIQUtJTkclMjAqJTJGJTVDbmltcG9ydCUyME1hdGglMjBmcm9tJTIwJy4lMkZtYXRocy5qcyclM0IlNUNuJTVDbmNvbnNvbGUubG9nKCUyME1hdGguY3ViZSglMjA1JTIwKSUyMCklM0IlMjAlMkYlMkYlMjAxMjUlMjIlMkMlMjJpc0VudHJ5JTIyJTNBdHJ1ZSU3RCUyQyU3QiUyMm5hbWUlMjIlM0ElMjJtYXRocy5qcyUyMiUyQyUyMmNvZGUlMjIlM0ElMjIlMkYlMkYlMjBtYXRocy5qcyU1Q24lNUNuY29uc3QlMjBzcXVhcmUlMjAlM0QlMjBmdW5jdGlvbiUyMCh4KSUyMCU3QiU1Q24lNUN0cmV0dXJuJTIweCUyMColMjB4JTNCJTVDbiU3RCUzQiU1Q24lNUNuY29uc3QlMjBjdWJlJTIwJTNEJTIwZnVuY3Rpb24lMjAoeCklMjAlN0IlNUNuJTVDdHJldHVybiUyMHglMjAqJTIweCUyMColMjB4JTNCJTVDbiU3RCU1Q24lNUNuZXhwb3J0JTIwJTdCJTIwc3F1YXJlJTJDJTIwY3ViZSUyMCU3RCUzQiU1Q24lNUNuZXhwb3J0JTIwZGVmYXVsdCUyMCU3QiU1Q24lMjAlMjBzcXVhcmUlMkMlNUNuJTIwJTIwY3ViZSU1Q24lN0QlMjIlMkMlMjJpc0VudHJ5JTIyJTNBZmFsc2UlN0QlNUQlMkMlMjJvcHRpb25zJTIyJTNBJTdCJTIyZm9ybWF0JTIyJTNBJTIyZXMlMjIlMkMlMjJuYW1lJTIyJTNBJTIybXlCdW5kbGUlMjIlMkMlMjJhbWQlMjIlM0ElN0IlMjJpZCUyMiUzQSUyMiUyMiU3RCUyQyUyMmdsb2JhbHMlMjIlM0ElN0IlN0QlN0QlMkMlMjJleGFtcGxlJTIyJTNBbnVsbCU3RA==))

略微改善是为什么?

React 在优势上的措辞非常谨慎,它说

可能会 略微改善 bundle 的大小

这是因为,无论是 ESM 还是 CJS 对同一模块的导入,都是存在缓存的,也就是说你在 A 组件全量引用了 React,在 B 组件再引用一次是不会在打包文件中再加上一个 React并不是 1 + 1 的关系

// A
import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}
// B
import React from 'react';

function App() {
  return <h1>Hello World</h1>;
}
// React 只会在磁盘被加载一次,往后都是缓存

性能优化和简化

其实 JSX runtime 对于 React.createElement 还有一定的性能优化和简化,但文章中没讲,所以我也不讲(绝对不是因为我菜而看不懂),相关链接 - 性能优化和简化

IMG

参考资料

  1. 介绍全新的 JSX 转换 - React - Luna Ruan
相关文章
|
5月前
|
前端开发 JavaScript 开发者
React:JSX语法入门
React:JSX语法入门
62 0
|
9天前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
25 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
2月前
|
XML JavaScript 前端开发
React Jsx语法入门
【8月更文挑战第13天】React Jsx语法入门
34 4
|
2月前
|
前端开发 JavaScript
React Server Component 使用问题之添加jsx的组件化能力,如何操作
React Server Component 使用问题之添加jsx的组件化能力,如何操作
|
2月前
|
前端开发 JavaScript 开发者
React组件与JSX之间的区别是什么
【8月更文挑战第9天】 React组件与JSX之间的区别是什么
50 4
|
2月前
|
XML 前端开发 JavaScript
【React新手必看】JSX,让你的代码舞动起来!
【8月更文挑战第24天】JSX(JavaScript XML)是React开发的核心,作为一种JavaScript语法扩展,它支持在代码中使用类似HTML的标签。JSX并非模板语言,在编译过程中转换为纯JavaScript,使React能高效更新DOM并渲染页面。其优势在于直观性、简洁性和灵活性:简化复杂用户界面构建、减少代码量,并支持HTML标签、React组件及JavaScript变量的表达式插入。基本用法涉及定义组件UI,如创建包含标题和段落的简单组件。
48 0
|
2月前
|
XML 前端开发 JavaScript
React——JSX语法【三】
React——JSX语法【三】
26 0
|
3月前
|
JavaScript 前端开发
react18【系列实用教程】JSX (2024最新版)
react18【系列实用教程】JSX (2024最新版)
49 1
|
3月前
|
前端开发 JavaScript 算法
react【框架原理详解】JSX 的本质、SyntheticEvent 合成事件机制、组件渲染过程、组件更新过程
react【框架原理详解】JSX 的本质、SyntheticEvent 合成事件机制、组件渲染过程、组件更新过程
54 0
|
5月前
|
XML 前端开发 JavaScript
react中JSX的详解
react中JSX的详解
45 2
下一篇
无影云桌面