JSX深度解析:不是HTML,勝似HTML的語法糖

JSX深度解析:不是HTML,勝似HTML的語法糖

作者:碼力無邊

大家好!我是依然在代碼世界里乘風破浪的碼力無邊。歡迎回到我們的《React奇妙之旅》第二站!

在上一篇文章中,我們成功地用Vite啟動了第一個React應用,并且在App.jsx這個文件里,大搖大擺地寫下了這樣的代碼:

return (<div><h1>Hello, CSDN!</h1></div>
);

當時,我告訴你這叫JSX。但你的心里一定有個大大的問號:“等一下!這明明就是在JavaScript文件里寫HTML,這種‘跨界混搭’真的合法嗎?這背后到底藏著什么秘密?”

問得好!這種刨根問底的精神,是成為頂尖工程師的關鍵。今天,我們就化身“代碼考古學家”,一起挖出JSX的“真實身份”。準備好你的放大鏡,我們將揭開這個“語法糖”甜蜜外衣下的硬核真相!

第一章:JSX的“真面目”—— 一場美麗的“騙局”

首先,我必須告訴你一個顛覆你認知的事實:瀏覽器根本不認識JSX!

是的,你沒聽錯。如果你把含有JSX的代碼直接扔進瀏覽器的<script>標簽里,它會毫不留情地給你一個語法錯誤(Uncaught SyntaxError)。

那為什么我們在Vite項目里寫JSX卻安然無事呢?

因為我們有一個“超級翻譯官”在幕后默默工作。這個翻譯官,就是Babel。Vite內部集成了Babel,它的核心工作之一,就是把我們寫的、人類可讀性極強的JSX,轉換(編譯)成瀏覽器能看懂的、純粹的JavaScript代碼。

那么,JSX到底被翻譯成了什么呢?讓我們來看一個最簡單的例子。

我們寫的JSX是這樣的:

const element = <h1>你好,世界</h1>;

經過Babel的“翻譯”后,它變成了這樣:

const element = React.createElement('h1',null,'你好,世界'
);

真相大白!

原來,我們寫的每一個JSX標簽,最終都會被轉換成一個React.createElement()函數調用。這個函數是React庫的核心部分,它會創建一個JavaScript對象(我們稱之為“React元素”),用來描述UI應該長什么樣。

React.createElement()函數接受的參數通常是:

  1. type:元素的類型。可以是一個字符串(如'h1', 'div'代表HTML標簽),也可以是另一個React組件。
  2. props:一個包含元素屬性的對象。比如classNameid等。如果沒有屬性,就是null
  3. ...children:元素的子節點。可以是文本、其他React元素,或者更多子元素。

讓我們看個復雜點的例子:

// 我們寫的JSX
const element = (<div className="greeting"><h1>你好!</h1><p>歡迎來到React的世界。</p></div>
);// Babel翻譯后的JS
const element = React.createElement('div',{ className: 'greeting' },React.createElement('h1', null, '你好!'),React.createElement('p', null, '歡迎來到React的世界。')
);

現在,你明白了嗎?JSX本質上就是 React.createElement() 的語法糖(Syntactic Sugar)。

它就像咖啡里的方糖,咖啡本身(純JavaScript)也能喝,但加了糖(JSX)之后,口感(開發體驗)會好上幾個數量級。它讓我們能用一種更直觀、更接近最終UI結構的方式來聲明界面,而不是去手動調用那些冗長、嵌套的函數。

這就是為什么我們說React是聲明式的。我們用JSX聲明了“我想要一個包含h1和p的div”,而不是用命令式的document.createElement一步步去操作DOM。

第二章:JSX的“五大黃金法則”—— 新手避坑指南

既然知道了JSX的本質,我們就要學習如何正確地使用它。就像學習一門新語言,掌握了基本語法和規則,才能寫出優美的“文章”。我為你總結了五條“黃金法則”,掌握了它們,你就能避免90%的JSX初級錯誤。

法則一:萬物歸一,必須有一個根元素

這是新手最常犯的錯誤。當你嘗試返回多個并列的元素時,React會報錯。

? 錯誤示范:

function UserProfile() {return (<h1>張三</h1><p>一位前端工程師</p>// Uncaught SyntaxError: Adjacent JSX elements must be wrapped in an enclosing tag.);
}

為什么會錯? 回想一下JSX的本質。上面的代碼會被翻譯成兩個并列的React.createElement()調用,但一個組件的return語句只能返回一個值。你不能return value1, value2;

? 正確姿勢:
用一個父元素(比如<div>)把它們包裹起來,確保只返回一個“根”。

function UserProfile() {return (<div><h1>張三</h1><p>一位前端工程師</p></div>);
}

“但我不想在頁面上增加一個多余的div層級怎么辦?”

好問題!React為我們提供了一個“隱形的包裹”—— Fragment (片段)

import React from 'react'; // 早期需要引入,現在大部分構建工具會自動處理function UserProfile() {return (<React.Fragment><h1>張三</h1><p>一位前端工程師</p></React.Fragment>);
}

React.Fragment在最終渲染到DOM時,它自身是不會出現的,非常干凈。它還有一個更簡潔的語法糖——空標簽 <> ... </>

function UserProfile() {return (<><h1>張三</h1><p>一位前端工程師</p></>);
}

記住: 每個組件返回的JSX,都必須像一個打包好的快遞,只能有一個最外層的包裹。

法則二: {} 花括號——通往JavaScript世界的“傳送門”

這是JSX最神奇的地方。在JSX中,你可以使用花括號{}來嵌入任何JavaScript表達式

“表達式”是指任何可以計算出一個值的代碼片段。

function App() {const user = {name: "碼力無邊",avatarUrl: "some-url.jpg", // 假設的URLage: 18, // 永遠18歲};const a = 10;const b = 20;function formatGreeting(name) {return `你好,尊敬的 ${name}!`;}return (<>{/* 1. 嵌入變量 */}<h1>{user.name}</h1>{/* 2. 嵌入屬性 (注意字符串要加引號) */}<img src={user.avatarUrl} alt="頭像" />{/* 3. 嵌入算術運算 */}<p>{a} + {b} = {a + b}</p>{/* 4. 嵌入函數調用 */}<footer>{formatGreeting(user.name)}</footer>{/* 5. 嵌入三元運算符,實現簡單邏輯 */}<p>用戶狀態:{user.age >= 18 ? "成年人" : "未成年"}</p></>);
}

注意: 你不能在{}里寫if...else語句或者for循環,因為它們是語句 (Statement),而不是表達式 (Expression)。但你可以用三元運算符? :來代替簡單的if...else。更復雜的邏輯我們稍后會講。

法則三:屬性命名,入鄉隨俗用“駝峰”

在HTML中,我們習慣用小寫或者用短橫線連接(kebab-case),比如classonclickfont-size。但在JSX中,事情有點不一樣。

  • class 變成 className
    這是最特殊也是最重要的一個。因為class是JavaScript中的保留關鍵字(用于定義類),為了避免沖突,React規定必須使用className來指定CSS類。

  • for 變成 htmlFor
    同樣,for是JS中的循環關鍵字,在<label>標簽中要用htmlFor

  • 其他屬性使用小駝峰命名法 (camelCase)
    HTML中的onclick在JSX中是onClickonmouseoveronMouseOver。所有事件相關的屬性都是這樣。

? 示例:

function LoginForm() {function handleClick(event) {event.preventDefault(); // 阻止表單默認提交行為console.log("按鈕被點擊了!");}return (<form className="login-form"><label htmlFor="username">用戶名:</label><input type="text" id="username" /><button onClick={handleClick}>登錄</button></form>);
}
法則四:樣式(Style),一個“對象”的藝術

想給JSX元素添加行內樣式?在HTML里我們寫字符串 style="color: red; font-size: 16px;"。但在JSX中,style屬性接受的是一個JavaScript對象

? 正確姿勢:

function StyledText() {// 1. 定義一個樣式對象const myStyle = {color: 'white',backgroundColor: 'dodgerblue', // CSS的 background-color -> JS的 backgroundColorpadding: '10px',borderRadius: '5px' // CSS的 border-radius -> JS的 borderRadius};return (// 2. 將樣式對象傳給style屬性<div style={myStyle}>這是一個帶樣式的div</div>);
}

你可能更常見到一種“雙花括號”的寫法,它只是把上面兩步合二為一了:

function StyledTextInline() {return (<div style={{ color: 'white', backgroundColor: 'purple', padding: '10px' }}>這也是一個帶樣式的div</div>);
}

解密雙花括號{{...}}

  • 第一層{}:表示這里是JSX的“JS傳送門”。
  • 第二層{}:表示我們傳入的是一個JavaScript對象

重點: 樣式對象的屬性名也必須使用小駝峰命名法。

法則五:注釋的正確“隱藏”方式

在JSX中寫注釋,也需要用{}包裹起來。

function CommentExample() {return (<div>{/* 這是JSX中的單行注釋 */}<h1>我的標題</h1>{/*這是多行注釋*/}<p>我的段落。</p></div>);
}

把它當成是在“JS傳送門”里寫標準的JavaScript注釋就可以了。直接寫HTML的<!-- ... -->注釋是不會生效的!

第三章:JSX的“進階魔法”—— 動態UI的秘密

掌握了基本法則,我們來看看如何用JSX施展一些更高級的“魔法”,讓我們的界面真正“動”起來。

魔法一:條件渲染 (Conditional Rendering)

我們經常需要根據不同的條件,顯示不同的內容。比如用戶登錄了,就顯示“歡迎回來”,沒登錄,就顯示“請登錄”。

在JSX中實現條件渲染,有幾種優雅的方式:

1. 使用三元運算符 (Ternary Operator)
最適合 “二選一” 的場景。

function Greeting({ isLoggedIn }) { // { isLoggedIn } 是Props,我們下一篇會講return (<div>{isLoggedIn ? <h1>歡迎回來!</h1> : <h1>請登錄</h1>}</div>);
}

2. 使用邏輯與 && 運算符
適合 “滿足條件就顯示,不滿足就不顯示” 的場景。

function Mailbox({ unreadMessages }) {const count = unreadMessages.length;return (<div><h1>你好!</h1>{count > 0 &&<h2>你有 {count} 條未讀消息。</h2>}</div>);
}

原理揭秘: 在JavaScript中,true && expression 總是返回 expression,而 false && expression 總是返回 false。React在渲染時,會忽略falsenullundefined這些“空”值,所以當count > 0false時,整個表達式的結果是false<h2>標簽就不會被渲染。

?? 注意一個坑: 不要讓 && 左邊的表達式返回0。因為0 && expression會返回0,React會把數字0渲染到頁面上!

3. 在return外部使用if/else
當邏輯非常復雜時,把邏輯判斷放在return語句的外面,會讓代碼更清晰。

function LoginButton({ userStatus }) {let button;if (userStatus === 'loggedIn') {button = <button>退出</button>;} else if (userStatus === 'loggingIn') {button = <button disabled>登錄中...</button>;} else {button = <button>登錄</button>;}return <div>{button}</div>;
}

這種方式可讀性最強,尤其適合多分支的復雜邏輯。

魔法二:列表渲染(List Rendering)—— .map() 的舞臺

如果后端給了我們一個數組,我們想把它渲染成一個列表,怎么辦?總不能一個一個手寫吧?這時候,JavaScript數組的.map()方法就成了我們的神器。

.map()方法會遍歷數組的每一項,并根據你提供的函數,返回一個新的數組。這和React的思想完美契合!

function PostList() {const posts = [{ id: 1, title: '我的第一篇React文章' },{ id: 2, title: '深入理解JSX' },{ id: 3, title: 'Props與State的愛恨情仇' },];return (<ul>{posts.map(post => (<li key={post.id}>{post.title}</li>))}</ul>);
}

代碼解析:

  1. 我們用{}開啟JS模式。
  2. posts.map(...)遍歷了posts數組。
  3. 對于數組中的每一個post對象,我們都返回一個JSX元素<li>
  4. .map()執行完畢后,會生成一個新的JSX元素數組 [<li...>, <li...>, <li...>]
  5. React拿到這個數組后,就會把里面的每一個li元素依次渲染出來。

一個神秘的key屬性:
你注意到每個li上都有一個key={post.id}屬性嗎?這是什么?

key是React用來識別列表中每個元素的“身份證”。 當列表內容發生變化時(比如增加、刪除、排序),React會根據key來判斷哪個元素是哪個,從而進行最高效的DOM更新,而不是粗暴地重新渲染整個列表。

key的法則:

  • key在兄弟元素之間必須是唯一的。
  • key應該是穩定的。不要使用Math.random()或數組的索引index作為key(除非列表是純靜態的),因為它們在列表項重新排序時會變化,導致性能問題和潛在的bug。
  • 使用數據本身自帶的唯一標識(如post.id)是最佳實踐。

關于key的重要性,我們后面會有專門的文章深入探討,現在你只需要記住:渲染列表時,務必給每一項加上一個穩定且唯一的key

總結:JSX,不僅僅是“糖”

今天,我們對JSX進行了一次徹底的“解剖”。讓我們回顧一下核心要點:

  1. JSX的本質:它是React.createElement()的語法糖,最終會被Babel編譯成純JavaScript對象。
  2. 五大黃金法則:單一根元素、{}嵌入表達式、className與駝峰屬性、style是個對象、以及正確的注釋方式。
  3. 兩大進階魔法:通過條件渲染讓UI“會思考”,通過列表渲染讓UI能處理批量數據。

JSX的設計,是React成功的關鍵之一。它巧妙地將UI的結構(HTML-like)、**樣式(CSS-in-JS)邏輯(JavaScript)**聚合在了組件這個最小單元內,實現了真正的高內聚。

現在,你已經不再是一個只會“照貓畫虎”寫JSX的初學者了。你理解了它的原理,掌握了它的規則,甚至學會了它的一些高級用法。你手中的“積木”,已經變得更加強大和靈活。

但是,我們目前的組件都還是“自給自足”的“孤島”。如何讓這些組件互相通信、傳遞信息,從而組合成一個有機的整體呢?

這就要引出我們下一篇文章的主角——Props!它就像組件之間的“信使”,負責傳遞數據和指令。準備好接收你的第一封“組件信件”了嗎?

我是碼力無邊,如果你覺得這篇文章對你有幫助,別忘了點贊收藏!有任何問題,歡迎在評論區與我交流。我們下一站,Props的世界見!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94550.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94550.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94550.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大模型應用新趨勢:從思維鏈到 HTML 渲染的破局之路

一、大模型交互范式的演進&#xff1a;從 Prompt 工程到思維鏈革新早期的 Prompt 工程曾面臨 “模型特異性” 困境 —— 精心設計的提示詞在不同模型上效果迥異。但隨著 ** 思維鏈&#xff08;CoT&#xff09;** 技術的成熟&#xff0c;這一局面正在改變。從 OpenAI o1 的隱式整…

從“找不到”到“秒上手”:金倉文檔系統重構記

你是否曾在浩如煙海的產品手冊中迷失方向&#xff1f;是否為了一個關鍵參數翻遍十幾頁冗余說明&#xff1f;是否對時靈時不靈的搜索功能感到抓狂&#xff1f;甚至因為漫長的加載時間而失去耐心&#xff1f;我們懂你!這些曾困擾金倉用戶的文檔痛點&#xff0c;從現在起&#xff…

【開源項目分享】可監控電腦CPU、顯卡、內存等硬件的溫度、功率和使用情況

系列文章目錄 【開源項目分享】可監控電腦CPU、顯卡、內存等硬件的溫度、功率和使用情況 &#xff08;一&#xff09;開源的硬件監控工具 LibreHardwareMonitor &#xff08;二&#xff09;LibreHardwareMonitor 分層架構設計 &#xff08;三&#xff09;LibreHardwareMonitor…

帕累托優化:多目標決策的智慧與藝術

本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 在相互沖突的目標中尋找最優平衡 ? 1. 帕累托優化概述 帕累托優化&a…

#Linux內存管理學以致用# 請你根據linux 內核struct page 結構體的雙字對齊的設計思想,設計一個類似的結構體

Linux struct page 的雙字對齊設計思想1.雙字對齊&#xff08;8字節對齊&#xff09;&#xff1a;確保struct page的大小是sizeof(long)的整數倍&#xff08;通常8字節&#xff09;&#xff0c;便于CPU高效訪問。減少內存碎片&#xff0c;提高緩存行&#xff08;Cache Line&…

白酒變局,透視酒企穿越周期之道

今年以來&#xff0c;在科技股的帶動下&#xff0c;A股市場表現十分突出&#xff0c;近期滬指甚至創出了十年來新高。然而&#xff0c;在這輪市場的表現中&#xff0c;曾經被資金熱捧的白酒板塊&#xff0c;卻顯得有些沉寂。業績層面&#xff0c;從目前已披露的白酒上市公司半年…

智慧園區:從技術賦能到價值重構,解鎖園區運營新范式

在數字化浪潮席卷產業的當下&#xff0c;智慧園區已從 “概念藍圖” 落地為 “實戰方案”&#xff0c;其核心邏輯既源于技術的突破性應用&#xff0c;也扎根于企業的實際需求&#xff0c;更順應著行業發展的未來趨勢&#xff0c;成為驅動園區從傳統管理向智能化運營升級的核心引…

模運算(密碼學/算法)

1 什么是模運算 模運算的概念 模運算是一種算術運算&#xff0c;常寫作a mod n&#xff0c;表示整數a除以正整數n后的余數。 模數是模運算中的除數n&#xff0c;它決定了結果的范圍。 公式表達&#xff1a; 對于任意整數a和正整數n&#xff0c;可以將a表示為&#xff1a;a qn …

海康相機的 HB 模式功能詳解

海康相機的 HB 模式是一種無損壓縮技術,全稱為High Bandwidth 模式,主要用于提升工業相機在高速場景下的數據傳輸效率。其核心原理是通過硬件級無損壓縮算法對原始圖像數據進行壓縮,在不損失畫質的前提下減少數據量,從而突破千兆網絡的帶寬限制,實現更高的行頻和傳輸幀率。…

electron應用開發:命令npm install electron的執行邏輯

我們來徹底解析 npm install electron 這個命令背后的完整執行邏輯。這是一個非常精妙的過程&#xff0c;遠不止下載一個簡單的 JavaScript 包那么簡單。理解了它&#xff0c;你就能透徹地明白 Electron 開發環境的運作原理&#xff0c;并能輕松解決各種安裝問題。 npm instal…

Visual Studio 2022不同項目設置不同背景圖

ClaudiaIDE Visual Studio 地址&#xff1a;https://marketplace.visualstudio.com/items?itemNamekbuchi.ClaudiaIDE&ssrfalse#overviewgithub 地址&#xff1a;https://github.com/buchizo/ClaudiaIDE/ 這是一個Visual Studio擴展&#xff0c;可以讓你設置自定義背景圖…

React頁面使用ant design Spin加載遮罩指示符自定義成進度條的形式

React頁面使用ant design Spin加載遮罩指示符自定義成進度條的形式具體實現&#xff1a;import React, { useState, useEffect, } from react; import { Spin, Progress, } from antd; import styles from ./style.less;const App () > {// 全局加載狀態const [globalLoadi…

TCP并發服務器構建

TCP并發服務器構建&#xff1a; 單循環服務器&#xff1a;服務端同一時刻只能處理單個客戶端的任務 并發服務器&#xff1a;服務端同一時刻能夠處理多個客戶端的任務 產生多個套接字可建立多個連接&#xff1a;TCP服務端并發模型&#xff1a; 1&#xff1a;使用多進程 頭文件&a…

優選算法-常見位運算總結

1.基礎位運算&#xff1a; >> :右移運算符&#xff1a; 邏輯右移&#xff08;無符號數&#xff09;&#xff1a;高位補 0&#xff0c;低位直接丟棄。 示例&#xff1a;8 >> 2&#xff08;二進制 1000 右移 2 位&#xff09;結果為 0010&#xff08;十進制 2&#…

記一次MySQL數據庫的操作練習

數據庫基礎使用數據庫的操作&#xff1a;1.使用命令行連接數據庫。在命令行鍵入”mysql -u root -p”命令。2.列出MySQL數據庫管理系統的數據庫列表。在命令行鍵入”show databases;”命令。3.創建數據庫。在命令行鍵入”create database database_name;”命令。使用”show dat…

C++STL-list 底層實現

目錄 一、實現框架 二、list_node節點類的模擬實現 節點構造函數 三、list_iterator迭代器的模擬實現 迭代器類的模板參數說明 構造函數 *運算符重載 運算符的重載 --運算符的重載 運算符的重載 !運算符的重載 list的模擬實現 默認成員函數 構造函數 拷貝構造函…

解決網站圖片加載慢:從架構原理到實踐

在當前的數字商業環境中&#xff0c;用戶的在線體驗至關重要。當一個潛在客戶訪問企業網站或電商平臺時&#xff0c;如果頁面加載過程遲緩&#xff0c;特別是圖片和視頻內容無法快速顯示&#xff0c;用戶的耐心會迅速耗盡。研究數據表明&#xff0c;網站加載時間與用戶跳出率和…

windows注冊表:開機自啟動程序配置

目錄 一、注冊表位置 系統范圍的開機自啟動程序 當前用戶的開機自啟動程序 二、配置步驟 三、注意事項 四、其他方法 任務計劃程序 啟動文件夾 1. 創建程序快捷方式 2. 打開 Startup 文件夾 3. 將快捷方式移動到 Startup 文件夾 4. 驗證程序是否自動啟動 注意事項 …

(11)用于無GPS導航的制圖師SLAM(一)

文章目錄 前言 1 安裝 RPLidar 和 Pixhawk 2 檢查 RPLidar 的串行端口 3 安裝更多軟件包 4 創建Catkin工作空間 5 安裝 RPLidar 節點 6 安裝 Google Cartographer 前言 本頁展示了如何使用 RPLidarA2 激光雷達(RPLidarA2 lidar)設置 ROS 和 Google Cartographer SLAM&a…

車載診斷架構 --- 基于整車功能的正向診斷需求開發

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…