成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

「深入淺出」實(shí)現(xiàn)JSX的轉(zhuǎn)換

開發(fā) 前端
當(dāng)我們?cè)陧?xiàng)目中使用 React 構(gòu)建界面時(shí),主要使用的就是 React 包。它提供了開發(fā)者需要的所有API。如React.Component、React.createElement、React.useState等等,所以它也是大多數(shù) React 項(xiàng)目的基礎(chǔ)。

前言

由于近期在看React框架源碼、底層實(shí)現(xiàn)方面的知識(shí),所以想把學(xué)習(xí)心得整理出來。

這也是一個(gè)新的系列「從0實(shí)現(xiàn)React 18核心模塊」的第一篇。

接下來還會(huì)更新:render、commit階段的實(shí)現(xiàn),以及Hooks架構(gòu)、useState、useEffect、單雙節(jié)點(diǎn)Diff的過程還有React 18中的并發(fā)更新原理。

在看文章之前,我們可以先想幾個(gè)問題:

  • JSX 是什么語法?
  • JSX 有什么優(yōu)勢(shì),它的轉(zhuǎn)換規(guī)則是什么或者它內(nèi)部是如何實(shí)現(xiàn)的?
  • 既然 React 一直在使用 JSX,那它的實(shí)現(xiàn)被寫應(yīng)該寫在哪個(gè)包里(比如react、react-dom,react-reconciler)?
  • 在React 17之前和React 17之后,JSX轉(zhuǎn)換的方法實(shí)現(xiàn)有哪些異同?
  • 如何實(shí)現(xiàn)React.createElement方法和運(yùn)行時(shí)的 jsx 方法?
  • 寫一個(gè)Demo引入自己實(shí)現(xiàn)的jsx方法,看看運(yùn)行結(jié)果

下文提到的 big-react 是從0到1實(shí)現(xiàn)的React的核心功能模塊原理的項(xiàng)目

如果自己實(shí)現(xiàn)一個(gè) React 框架,它需要包含哪些內(nèi)置的包:

  • react包是 React 的核心庫,提供了創(chuàng)建和管理組件所需的基本功能(比如組件創(chuàng)建、組件生命周期管理、虛擬DOM以及Hooks等),主要是一些和宿主環(huán)境無關(guān)的方法。
  • react-reconciler包實(shí)現(xiàn)了 React 的 reconciliation 協(xié)調(diào)算法,是一種核心優(yōu)化策略的實(shí)現(xiàn),主要自定義協(xié)調(diào)器的實(shí)現(xiàn)。以及在不同的平臺(tái)或環(huán)境中使用 React。
  • shared包是big-react公用的輔助方法,和宿主環(huán)境無關(guān)。

如果還有一個(gè)必要的包,那就是react-dom:

  • react-dom:這個(gè)包提供了將 React 與 DOM(瀏覽器環(huán)境)集成的方法。它包含了用于將 React 組件渲染到 DOM 中的 ReactDOM.render() 函數(shù),以及其他與瀏覽器環(huán)境相關(guān)的實(shí)用功能。對(duì)于在瀏覽器中運(yùn)行的 React 應(yīng)用程序,react-dom 是必需的。

react與react-reconciler包是什么

react包為我們提供了什么

當(dāng)我們?cè)陧?xiàng)目中使用 React 構(gòu)建界面時(shí),主要使用的就是 react? 包。它提供了開發(fā)者需要的所有API。如React.Component、React.createElement、React.useState等等,所以它也是大多數(shù) React 項(xiàng)目的基礎(chǔ)。

react-reconciler包實(shí)現(xiàn)了什么?

react-reconciler包是一個(gè)更底層、更高級(jí)的庫,它實(shí)現(xiàn)了reconciliation協(xié)調(diào)算法,reconciliation是 React 的一種核心優(yōu)化策略,用于在更新組件時(shí)比較虛擬DOM樹的差異,并將實(shí)際更改應(yīng)用到實(shí)際的DOM樹。這有助于提高性能,因?yàn)楸苊饬瞬槐匾腄OM操作。

它主要用于創(chuàng)建自定義渲染器,以及在不同的平臺(tái)中去使用 React。例如,react-dom(用于Web平臺(tái))和react-native(用于移動(dòng)應(yīng)用)都使用react-reconciler作為底層庫,實(shí)現(xiàn)了針對(duì)各自平臺(tái)的渲染邏輯。

JSX 是什么

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

在React中,JSX是一種JavaScript語法擴(kuò)展,允許你在JavaScript代碼中編寫類似HTML的標(biāo)記。要使用JSX,需要在構(gòu)建過程中將其轉(zhuǎn)換為標(biāo)準(zhǔn)的JavaScript代碼。

通常,這個(gè)轉(zhuǎn)換過程包括兩個(gè)主要部分:

  • 編譯時(shí):通常指將 JSX 語法轉(zhuǎn)換為瀏覽器可以理解的普通 JavaScript 代碼的過程,這個(gè)過程通常由 Babel 完成。
  • 構(gòu)建時(shí):在將JSX語法轉(zhuǎn)換為標(biāo)準(zhǔn)的JavaScript代碼后,通常會(huì)使用構(gòu)建和打包工具(如Webpack、Rollup)對(duì)代碼進(jìn)行優(yōu)化、壓縮和打包。打包工具將源代碼和依賴項(xiàng)組合成一個(gè)或多個(gè)文件(“bundles”或“chunks”),用于在瀏覽器中運(yùn)行。
  • 運(yùn)行時(shí):React會(huì)根據(jù)編譯后的代碼創(chuàng)建虛擬DOM樹,然后將其渲染到實(shí)際的DOM中。還會(huì)發(fā)生的階段有狀態(tài)管理和更新、事件處理和Diff算法的比較等。

JSX 被 Babel 編譯成了什么

在React 17之前,JSX語法會(huì)被編譯成React.createElement函數(shù)的調(diào)用,用來創(chuàng)建虛擬DOM元素。

轉(zhuǎn)換結(jié)果如下:

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

從React 17開始,引入了新的JSX轉(zhuǎn)換功能,稱為"Runtime Automatic"(自動(dòng)運(yùn)行時(shí))。這意味著在使用JSX語法時(shí),不再需要手動(dòng)引入React庫。在自動(dòng)運(yùn)行時(shí)模式下,JSX會(huì)被轉(zhuǎn)換成新的入口函數(shù),import {jsx as _jsx} from 'react/jsx-runtime'; 和 import {jsxs as _jsxs} from 'react/jsx-runtime';。

轉(zhuǎn)換結(jié)果如下:

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

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

接下來我們就來實(shí)現(xiàn)jsx方法或React.createElement方法(包括dev、prod兩個(gè)環(huán)境)。

工作量包括:

  • 實(shí)現(xiàn)jsx方法
  • 實(shí)現(xiàn)打包流程
  • 實(shí)現(xiàn)調(diào)試打包結(jié)果的環(huán)境

實(shí)現(xiàn) jsx 轉(zhuǎn)換方法

jsx 轉(zhuǎn)換方法包括:

  • React.createElement方法
  • jsxDEV方法(dev環(huán)境)
  • jsx方法(prod環(huán)境)

實(shí)現(xiàn)React.createElement

在React 17之前,JSX轉(zhuǎn)換應(yīng)用的是createElement方法,下面是它的實(shí)現(xiàn):

/**
*
* @param type 元素類型
* @param config 元素屬性,包括key,不包括子元素children
* @param maybeChildren 子元素children
* @returns 返回一個(gè)ReactElement
*/
const createElement = (
type: ElementType,
config: any,
...maybeChildren: any
) => {
// reactElement 自身的屬性
let key: Key = null;
let ref: Ref = null;

// 創(chuàng)建一個(gè)空對(duì)象props,用于存儲(chǔ)屬性
const props: Props = {};

// 遍歷config對(duì)象,將ref、key這些ReactElement內(nèi)部使用的屬性提取出來,不應(yīng)該被傳遞下去
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方法的區(qū)別這里只體現(xiàn)在第三個(gè)參數(shù)上。

實(shí)現(xiàn)jsx方法

從React 17之后,JSX轉(zhuǎn)換應(yīng)用的是jsx方法,下面是它的實(shí)現(xiàn):

/**
*
* @param type 元素類型
* @param config 元素屬性
* @param maybeKey 可能的key值
* @returns 返回一個(gè)ReactElement
*/
const jsx = (type: ElementType, config: any, maybeKey: any) => {
// 初始化key和ref為空
let key = null;
let ref = null;

// 創(chuàng)建一個(gè)空對(duì)象props,用于存儲(chǔ)屬性
const props: Props = {};

// 遍歷config對(duì)象,將ref、key這些ReactElement內(nèi)部使用的屬性提取出來,不應(yīng)該被傳遞下去
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);
};

這段代碼定義了一個(gè)jsx函數(shù),主要用于創(chuàng)建React元素。首先,它會(huì)提取可能存在的key和ref屬性,并將剩余屬性添加到一個(gè)新的props對(duì)象中。最后用ReactElement函數(shù)創(chuàng)建一個(gè)React元素并返回。

從上面代碼中可以看到還實(shí)現(xiàn)了ReactElement方法:

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

// 為了不濫用 React.elemen,所以為它創(chuàng)建一個(gè)單獨(dú)的鍵
// 為React.element元素創(chuàng)建一個(gè) symbol 并放入到 symbol 注冊(cè)表中
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 =...

用自己實(shí)現(xiàn)的的jsx接入Demo

我們?cè)囍炎约簩?shí)現(xiàn)的jsx方法,創(chuàng)建一個(gè)ReactElement,看它是否能夠渲染在頁面上。

圖片

實(shí)現(xiàn)jsx方法

jsx-Demo運(yùn)行地址

jsx方法和createElement的區(qū)別

jsx函數(shù)和createElement函數(shù)都用于在React中創(chuàng)建虛擬DOM元素,但它們的語法和用法有所不同。jsx函數(shù)來自于React 17及更高版本中的新的JSX轉(zhuǎn)換功能,稱為"Runtime Automatic"。

以下是兩者之間的主要區(qū)別:

  1. 語法和轉(zhuǎn)換方式:jsx函數(shù)用于處理新的JSX轉(zhuǎn)換方式,其語法更簡(jiǎn)潔。createElement函數(shù)用于處理傳統(tǒng)的JSX轉(zhuǎn)換方式。

例如,一個(gè)JSX元素:

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

使用createElement轉(zhuǎn)換后的代碼如下:

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

使用jsx函數(shù)(自動(dòng)運(yùn)行時(shí))轉(zhuǎn)換后的代碼如下:

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

const element = _jsx("div", { className: "container", children: "Hello, world!" });
  1. ?子元素和key值處理:jsx函數(shù)將子元素作為屬性(children)傳遞,而createElement函數(shù)將子元素作為額外的參數(shù)傳遞。同時(shí)子元素上的key值在jsx函數(shù)中也會(huì)以第三個(gè)參數(shù)的形式傳遞,而在createElement函數(shù)中,則是存在于config第二個(gè)參數(shù)中。

在createElement函數(shù)中:

React.createElement("div", {className: "app", key: "appKey"}, "hello,app");

在jsx函數(shù)中:

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

_jsx("div", {className: "app", children: "hello,app"}, "appKey");
  1. ?兼容性和版本:createElement函數(shù)在所有React版本中可用,而jsx函數(shù)僅在React 17及更高版本中提供。盡管React團(tuán)隊(duì)推薦使用新的JSX轉(zhuǎn)換方式,但許多現(xiàn)有項(xiàng)目可能仍在使用createElement函數(shù)。

這時(shí)可能產(chǎn)生兩個(gè)疑問:

  • 從React 17之后使用Runtime Automatic自動(dòng)運(yùn)行時(shí)有什么好處?
  1. 簡(jiǎn)化組件代碼:不再需要在每個(gè)組件文件頂部添加**import React from 'react';**。這使得組件代碼更簡(jiǎn)潔,更易于閱讀和維護(hù)。
  2. 節(jié)省包大小:由于不再需要導(dǎo)入整個(gè)React對(duì)象,構(gòu)建工具可以更好地優(yōu)化輸出代碼,從而減小輸出包的大小。
  • 改成jsx函數(shù)后,為什么要把key屬性單獨(dú)拿出來放在第三個(gè)參數(shù)?

在之前的React版本中,每當(dāng)創(chuàng)建一個(gè)新的React元素時(shí),React都需要從屬性對(duì)象中提取key?和ref,這會(huì)導(dǎo)致額外的性能開銷。

將key?作為單獨(dú)的參數(shù)傳遞,可以讓React在處理虛擬DOM樹時(shí)更容易地訪問key,無需每次都從屬性對(duì)象中查找。這有助于提高React的性能和效率,特別是在處理大量元素和復(fù)雜組件樹時(shí)。

實(shí)現(xiàn)打包流程

打包流程稍微有些復(fù)雜,后續(xù)寫到文章里。

簡(jiǎn)單來說就是使用 Rollup,將編寫jsx方法的文件打包出來,通過pnpm link --global的方式生成一個(gè)全局的react包,這樣就可以通過pnpm link react --global調(diào)試自己創(chuàng)建的 create-react-app demo項(xiàng)目了。

圖片

構(gòu)建react包思路

責(zé)任編輯:姜華 來源: 前端時(shí)光屋
相關(guān)推薦

2021-04-27 08:54:43

ConcurrentH數(shù)據(jù)結(jié)構(gòu)JDK8

2021-03-16 08:54:35

AQSAbstractQueJava

2011-07-04 10:39:57

Web

2019-11-11 14:51:19

Java數(shù)據(jù)結(jié)構(gòu)Properties

2022-12-02 09:13:28

SeataAT模式

2009-11-30 16:46:29

學(xué)習(xí)Linux

2019-01-07 15:29:07

HadoopYarn架構(gòu)調(diào)度器

2021-07-20 15:20:02

FlatBuffers阿里云Java

2012-05-21 10:06:26

FrameworkCocoa

2017-07-02 18:04:53

塊加密算法AES算法

2022-09-26 09:01:15

語言數(shù)據(jù)JavaScript

2019-11-14 09:53:30

Set集合存儲(chǔ)

2009-12-25 15:49:43

Linux rescu

2022-01-11 07:52:22

CSS 技巧代碼重構(gòu)

2025-03-27 09:38:35

2012-02-21 13:55:45

JavaScript

2022-10-31 09:00:24

Promise數(shù)組參數(shù)

2018-11-09 16:24:25

物聯(lián)網(wǎng)云計(jì)算云系統(tǒng)

2022-11-09 08:06:15

GreatSQLMGR模式

2009-11-18 13:30:37

Oracle Sequ
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩在线欧美 | 成人在线免费电影 | 日韩精品中文字幕一区二区三区 | 黄色片在线 | h免费观看 | 欧美日韩亚洲国产 | 天色综合网 | 999久久 | 日本免费在线看 | 日韩毛片免费看 | 国产精品久久一区二区三区 | 亚洲欧洲成人av每日更新 | 在线免费观看黄色av | 精品1区2区3区 | chengrenzaixian| 天天看逼| 日本精品视频 | 久久久国产视频 | 日本不卡一区二区三区 | 成人免费视频网址 | 精品亚洲91 | 中文字幕在线观看第一页 | 日韩中出 | 精品视频久久久久久 | 欧美另类日韩 | 成人精品啪啪欧美成 | 999久久久久久久久6666 | 亚洲国产网址 | 999久久久 | 久久久久久一区 | 亚洲国产成人在线视频 | 色999视频 | 国产不卡视频 | 久草青青草 | 青青草网站在线观看 | 狠狠操你 | 亚洲成人国产精品 | 午夜影晥 | 爱综合| 日韩精品一区二区三区在线观看 | 成人性生交大片免费看r链接 |