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

「深入淺出」實現JSX的轉換

開發 前端
當我們在項目中使用 React 構建界面時,主要使用的就是 React 包。它提供了開發者需要的所有API。如React.Component、React.createElement、React.useState等等,所以它也是大多數 React 項目的基礎。

前言

由于近期在看React框架源碼、底層實現方面的知識,所以想把學習心得整理出來。

這也是一個新的系列「從0實現React 18核心模塊」的第一篇。

接下來還會更新:render、commit階段的實現,以及Hooks架構、useState、useEffect、單雙節點Diff的過程還有React 18中的并發更新原理。

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

  • JSX 是什么語法?
  • JSX 有什么優勢,它的轉換規則是什么或者它內部是如何實現的?
  • 既然 React 一直在使用 JSX,那它的實現被寫應該寫在哪個包里(比如react、react-dom,react-reconciler)?
  • 在React 17之前和React 17之后,JSX轉換的方法實現有哪些異同?
  • 如何實現React.createElement方法和運行時的 jsx 方法?
  • 寫一個Demo引入自己實現的jsx方法,看看運行結果

下文提到的 big-react 是從0到1實現的React的核心功能模塊原理的項目

如果自己實現一個 React 框架,它需要包含哪些內置的包:

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

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

  • react-dom:這個包提供了將 React 與 DOM(瀏覽器環境)集成的方法。它包含了用于將 React 組件渲染到 DOM 中的 ReactDOM.render() 函數,以及其他與瀏覽器環境相關的實用功能。對于在瀏覽器中運行的 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 語法轉換為瀏覽器可以理解的普通 JavaScript 代碼的過程,這個過程通常由 Babel 完成。
  • 構建時:在將JSX語法轉換為標準的JavaScript代碼后,通常會使用構建和打包工具(如Webpack、Rollup)對代碼進行優化、壓縮和打包。打包工具將源代碼和依賴項組合成一個或多個文件(“bundles”或“chunks”),用于在瀏覽器中運行。
  • 運行時:React會根據編譯后的代碼創建虛擬DOM樹,然后將其渲染到實際的DOM中。還會發生的階段有狀態管理和更新、事件處理和Diff算法的比較等。

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 轉換方法

jsx 轉換方法包括:

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

實現React.createElement

在React 17之前,JSX轉換應用的是createElement方法,下面是它的實現:

/**
*
* @param type 元素類型
* @param config 元素屬性,包括key,不包括子元素children
* @param maybeChildren 子元素children
* @returns 返回一個ReactElement
*/
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方法,下面是它的實現:

/**
*
* @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);
};

這段代碼定義了一個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函數。

這時可能產生兩個疑問:

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

在之前的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包思路

責任編輯:姜華 來源: 前端時光屋
相關推薦

2021-04-27 08:54:43

ConcurrentH數據結構JDK8

2021-03-16 08:54:35

AQSAbstractQueJava

2011-07-04 10:39:57

Web

2009-11-30 16:46:29

學習Linux

2019-11-11 14:51:19

Java數據結構Properties

2022-12-02 09:13:28

SeataAT模式

2019-01-07 15:29:07

HadoopYarn架構調度器

2017-07-02 18:04:53

塊加密算法AES算法

2012-05-21 10:06:26

FrameworkCocoa

2021-07-20 15:20:02

FlatBuffers阿里云Java

2022-09-26 09:01:15

語言數據JavaScript

2012-02-21 13:55:45

JavaScript

2022-01-11 07:52:22

CSS 技巧代碼重構

2018-11-09 16:24:25

物聯網云計算云系統

2022-11-09 08:06:15

GreatSQLMGR模式

2022-10-31 09:00:24

Promise數組參數

2019-12-04 10:13:58

Kubernetes存儲Docker

2009-11-18 13:30:37

Oracle Sequ

2025-03-27 09:38:35

2019-11-14 09:53:30

Set集合存儲
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一区二区三区中文字幕 | 福利片在线看 | 日干夜操 | 超碰在线国产 | 成人av免费| 高清一区二区三区 | 高清一区二区三区 | 夜夜艹 | 日本黄色免费视频 | 亚洲第一成人影院 | 日日干夜夜干 | 狠狠干天天干 | 国产伦精品一区二区三区照片91 | 亚洲天堂一区二区 | 一区二区亚洲 | 99久久久99久久国产片鸭王 | 在线看成人av| 亚洲激情自拍偷拍 | 日韩精品 电影一区 亚洲 | 日韩精品久久一区二区三区 | 狠狠爱一区二区三区 | 欧美日日 | 午夜影院操| 成人av高清| 久久专区| 欧美在线a | 日批免费在线观看 | 日韩精品一区中文字幕 | av中文字幕在线 | 国产一区二区三区在线观看免费 | 国产在线高清 | 一级全黄少妇性色生活免费看 | 国产精品一区在线 | 日韩在线视频一区 | 综合色久| 一本色道久久综合亚洲精品高清 | 久久久精品国产 | 国产福利91精品 | 人人做人人澡人人爽欧美 | 亚洲精品区 | 国产一二三区精品视频 |