本系列將建立一個小型前端框架,其功能類似於 React,以說明 React 在幕後的工作原理。本章涵蓋 JSX。
我將使用 Bun 作為運行時。 Node 可能需要額外的配置來進行打字稿和編譯。
本教學是基於本教學課程,但使用了 JSX、Typescript 和更簡單的實作方法。您可以在我的 GitHub 儲存庫上查看註釋和程式碼。
現在,在我們進一步深入之前,讓我們先看看 React-jsx 的幾個重要元素。
如果你看過 React 元件的轉譯程式碼,你會發現它只是一堆函數呼叫。 JSX 只是 React.createElement 的語法糖。也就是說,例如,以下 JSX 程式碼:
const element = <h1 className="greeting">Hello, world!</h1>;
將被轉譯為:
const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
React.createElement 會建立一個虛擬元素,這是另一個核心機制。簡單來說,虛擬元素就是虛擬DOM中的元素。虛擬 DOM 是代表實際 DOM 的東西。由於操作虛擬DOM只是操作js對象,所以比操作實際DOM快很多。我們將在下一章討論虛擬 DOM。但就目前而言,知道 JSX 只是 React.createElement 的語法糖就足夠了。
React.createElement 函數依序接受以下參數,
這聽起來很容易,對吧?那我們就開始吧。
編譯時,我們可以指定要使用的函數-預設函數是React.createElement。但我們可以使用我們自己的函數。
所以我們建立一個v-dom.ts,以便先定義虛擬元素。
export type VDomAttributes = { key?: string | number [_: string]: string | number | boolean | Function | undefined } export interface VDomElement { kind: 'element' tag: string children?: VDomNode[] props?: VDomAttributes key: string | number | undefined } export type VDomNode = | string | VDomElement
請注意,我們在每個節點中都有一個關鍵欄位(節點只是文字或元素的名稱)。這是為了和解,我們將在下一章中討論。您現在可以安全地忽略它。
現在我們可以實作createElement函數了。我們把它放在同一個文件中。
export function createElement(tag: string, props: VDomAttributes, ...children: VDomNode[]): VDomElement { console.log('createElement', tag, props, children) return { kind: 'element', tag, children, props, key: props?.key ?? undefined } }
現在我們指示編譯器使用這個函數。我們可以透過將以下行新增到文件頂部來做到這一點。
const element = <h1 className="greeting">Hello, world!</h1>;
請注意,由於我們採用React標準,因此我們需要引入React類型定義。我們可以透過將以下行新增到文件頂部來做到這一點。
const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
然後在 tsconfig.json 中,我們將以下行新增至 compilerOptions 欄位。
export type VDomAttributes = { key?: string | number [_: string]: string | number | boolean | Function | undefined } export interface VDomElement { kind: 'element' tag: string children?: VDomNode[] props?: VDomAttributes key: string | number | undefined } export type VDomNode = | string | VDomElement
現在,我們可以看看我們建立的虛擬元素。
export function createElement(tag: string, props: VDomAttributes, ...children: VDomNode[]): VDomElement { console.log('createElement', tag, props, children) return { kind: 'element', tag, children, props, key: props?.key ?? undefined } }
您將看到我們基於 JSX 程式碼定義的虛擬 dom 元素。
此外,如果您不知道它的正式名稱,我們也可以定義一個片段元素 - >。我們可以透過將以下行新增到文件頂部來做到這一點。
在處理fragment時,編譯器會將配置好的fragment工廠帶到元素建立函數中的標籤中。這與函數式元件的工作方式相同——函數式元件會將函數傳遞給標籤,我們將在下一章中示範這一點。
儘管如此,在我們的實作中,不需要進行複雜的處理 - 我們只需要為片段設定一個特殊的標籤。
import { createElement as h } from './v-dom'
額外的編譯器選項,
bun i @types/react
基本上,fragment 只是一個有空標籤的特殊元素。當建立fragment時,編譯器會取得jsxFragmentFactory並將其放入createElement第一個參數的tag參數中。所以我們可以很容易地將片段與其他元素區分開來。
"compilerOptions": { "jsx": "react", "jsxFactory": "createElement", }
此程式碼將正確產生虛擬 DOM。到目前為止,我們已經實作了小型 React 的 JSX 部分。
呃,這是第三章的作者。事實上,目前 JSX 的實現並不完美。我們將在第三章修復它。現在它不支援諸如
之類的語法
import { createElement } from "./v-dom"; function App() { return <div> <h1>a</h1> <h2>b</h2> </div> } console.log(App());
這是因為每個 {} 都被視為一個子項,而映射則傳回一個陣列。所以它會有嵌套的孩子。
此外,我們還不支援功能組件,這將在下一章中介紹。
您可以按照目前的方式進行操作,稍後再修復。抱歉造成不便。
以上是建立一個小型 React ChSX的詳細內容。更多資訊請關注PHP中文網其他相關文章!