JSX คือส่วนขยายไวยากรณ์ของ JavaScript ที่มีลักษณะคล้าย XML พัฒนาโดยทีม React เพื่ออธิบายส่วนติดต่อผู้ใช้แบบเชิงประกาศ (declarative) ต่างจากเทมเพลตเอนจิน JSX จะถูกคอมไพล์เป็นการเรียกฟังก์ชัน JavaScript ทั่วไป ซึ่งให้พลังเต็มรูปแบบของภาษาในการเขียนมาร์กอัป จากข้อมูลของ React, 2024 JSX ถูกสนับสนุนในเฟรมเวิร์กและไลบรารี React สมัยใหม่ทั้งหมด
สิ่งสำคัญ
JSX (JavaScript XML) คือส่วนขยายไวยากรณ์ของ JavaScript ถูกนำเสนอครั้งแรกใน React ในปี 2013 มันช่วยให้นักพัฒนาอธิบายโครงสร้างของส่วนติดต่อผู้ใช้ในรูปแบบเชิงประกาศ โดยผสมผสานมาร์กอัปและตรรกะไว้ในไฟล์เดียว
ก่อนที่ JSX จะเกิดขึ้น นักพัฒนาใช้ เทมเพลตสตริง (Mustache, Handlebars) หรือสร้าง DOM element ด้วยตนเองผ่าน document.createElement ทั้งสองแนวทางมีข้อเสีย: เทมเพลตสตริงไม่มีการตรวจสอบชนิด และการสร้าง DOM ด้วยมือยุ่งยากและเกิดข้อผิดพลาดได้ง่าย
React เลือกเส้นทางที่ต่างออกไป — การฝังมาร์กอัปลงในโค้ดโดยตรง การตัดสินใจนี้ทำให้เกิดการถกเถียงในชุมชน แต่เมื่อเวลาผ่านไปก็กลายเป็นมาตรฐาน ทุกวันนี้ JSX ถูกใช้ไม่เพียงใน React แต่ยังใช้ใน SolidJS, Preact, Qwik และแม้แต่ใน Vue ผ่านปลั๊กอิน
JSX element มีลักษณะเหมือน HTML tag แต่ถูกฝังใน JavaScript โดยตรง แต่ละ element ถูกแปลงเป็นการเรียก React.createElement ด้วยอาร์กิวเมนต์สามตัว: ชนิดของ element, ออบเจกต์ของ props และ child element หนึ่ง root element เป็นกฎบังคับ: JSX expression ต้องคืนค่า parent element หนึ่งตัวหรือ fragment
import React from 'react';
// JSX พื้นฐาน - องค์ประกอบรากเดียว
const element = (
<div className="app">
<h1>Hello, World!</h1>
<p>Welcome to React</p>
</div>
);
เมื่อไม่ต้องการ DOM node ที่เกินจำเป็น ให้ใช้ React.Fragment หรือไวยากรณ์แบบสั้น <></> Fragment ไม่สร้าง DOM element จริง แต่เพียงจัดกลุ่ม child nodes ซึ่งมีประโยชน์โดยเฉพาะในตารางและรายการที่ wrapper ที่เกินจำเป็นจะทำให้โครงสร้าง HTML เสียหาย
function Columns() {
return (
<>
<td>First</td>
<td>Second</td>
</>
);
}
JavaScript expression ใด ๆ ถูกแทรกลงใน JSX ด้วยเครื่องหมายปีกกา { } ภายในสามารถใช้ตัวแปร การเรียกฟังก์ชัน ตัวดำเนินการ ternary และเมธอดของอาร์เรย์ การเรนเดอร์แบบมีเงื่อนไข ทำได้ด้วยตัวดำเนินการ ternary หรือ logical AND
function Greeting({ name, isLoggedIn }) {
const items = ['Red', 'Green', 'Blue'];
return (
<div>
{/* Expression with variable */}
<h1>Hello, {name}</h1>
{/* Conditional rendering */}
{isLoggedIn ?
<p>Welcome back!</p :
<button>Login</button>
}
{/* Render array */}
<ul>
{items.map(item =>
<li key={item}>{item}</li>
)}
</ul>
</div>
);
}
ข้อจำกัดสำคัญคือ ภายในเครื่องหมายปีกกาสามารถใช้ได้เฉพาะ นิพจน์ ไม่ใช่คำสั่ง (statement) ไม่สามารถใช้ if/else, for, switch ได้ — ใช้ได้เฉพาะตัวดำเนินการ ternary ตัวดำเนินการทางตรรกะ และเมธอดของอาร์เรย์ การเรียกฟังก์ชันทำได้ รวมถึง arrow function
JSX ดูคล้าย HTML แต่มีความแตกต่างโดยพื้นฐาน แอตทริบิวต์ เขียนแบบ camelCase — className แทน class, onClick แทน onclick, tabIndex แทน tabindex เนื่องจาก JSX ถูกแปลงเป็น JavaScript ซึ่งไม่อนุญาตให้ใช้เครื่องหมายยัติภังค์ (hyphen) ในชื่อคุณสมบัติ
| HTML | JSX | สาเหตุ |
|---|---|---|
class="wrapper" | className="wrapper" | class เป็นคำสงวนใน JS |
onclick="handle()" | onClick={handle} | camelCase สำหรับคุณสมบัติ JS |
style="color: red" | style={{ color: 'red' }} | ออบเจกต์สไตล์แทนสตริง |
<input disabled> | <input disabled={true}> | ระบุ boolean attribute อย่างชัดเจน |
อีกหนึ่งความแตกต่างที่สำคัญคือ self-closing tag ใน HTML บางแท็ก (br, hr, input) ไม่จำเป็นต้องปิด ใน JSX แท็กที่ self-closing ทั้งหมดต้องมีเครื่องหมาย slash ปิด: <br />, <input /> ชื่อของ SVG attribute ก็ถูกแปลงเป็น camelCase เช่นกัน: strokeWidth, fillOpacity
เบราว์เซอร์ไม่เข้าใจ JSX โดยตรง ก่อนทำงาน JSX code ต้องผ่าน transpiler — Babel, TypeScript หรือ esbuild แต่ละ JSX element ถูกแปลงเป็นการเรียก createElement หรือ jsx/runtime
// JSX ต้นฉบับ
const element = <h1 className="title">Hello</h1>;
// หลังการแปลง
const element = React.createElement(
'h1',
{ className: 'title' },
'Hello'
);
ตั้งแต่ React 17 มีการแปลง JSX แบบใหม่ที่ไม่ต้อง import React การ import แบบอัตโนมัติ ของ jsx-runtime ช่วยให้เขียน component ได้โดยไม่ต้องใช้ import React from 'react' Babel และ TypeScript สนับสนุนการแปลงนี้ผ่านการตั้งค่า {"runtime": "automatic"}
แม้ JSX จะถูกเชื่อมโยงกับ React แต่เฟรมเวิร์กอื่น ๆ ก็ใช้มันเช่นกัน SolidJS ใช้ JSX พร้อมการคอมไพล์เป็น DOM node จริง โดยไม่มี Virtual DOM Preact เป็นทางเลือกที่เบากว่า React ที่สนับสนุน JSX อย่างเต็มที่ Vue สนับสนุน JSX ผ่าน @vue/babel-plugin-jsx แม้ template จะเป็นรูปแบบหลักของมัน
ความแตกต่างที่สำคัญคือ semantics ของนิพจน์ ใน React JSX expression จะถูกทำงานทุกครั้งที่เรนเดอร์ ใน SolidJS JSX ถูกคอมไพล์เพียงครั้งเดียว และการอัปเดตถูกติดตามผ่านสัญญาณ (signals) สิ่งนี้ส่งผลต่อประสิทธิภาพ: SolidJS ไม่ต้องใช้ Virtual DOM และ reconciliation ดังนั้นการเรนเดอร์ JSX จึงเร็วกว่า
ใน TypeScript JSX ถูกรองรับในระดับภาษา ไฟล์ที่มีนามสกุล .tsx จัดการไวยากรณ์ JSX โดยอัตโนมัติ การกำหนดชนิดของ props เป็นหนึ่งในข้อได้เปรียบหลักของ TypeScript + JSX: IDE แนะนำแอตทริบิวต์ที่คาดหวัง ชนิดของ callback และความจำเป็นของ props
เขียน JSX code ที่อ่านง่ายและดูแลรักษาง่าย ดึงนิพจน์ที่ซับซ้อนออกไป ใส่ตัวแปรหรือฟังก์ชันแยก — ช่วยให้อ่านง่ายขึ้นและทดสอบตรรกะแยกจากมาร์กอัปได้ ใช้ fragment แทนการห่อ div ที่เกินจำเป็น
กฎการจัดตาราง (tab) — รักษาระดับการเยื้อง (indent) สำหรับ element ที่ซ้อนกัน แต่ละระดับการซ้อนคือหนึ่งระดับการเยื้อง แท็กปิดต้องอยู่ในระดับเดียวกับแท็กเปิด สำหรับแอตทริบิวต์ที่ยาวให้ใช้การขึ้นบรรทัดใหม่
หลีกเลี่ยง inline arrow function ใน props ถ้ามันถูกสร้างขึ้นทุกครั้งที่เรนเดอร์ ในกรณีเช่นนี้ให้ห่อ callback ด้วย useCallback หรือย้ายฟังก์ชันออกไปนอก component แบ่ง component ที่ซับซ้อนเป็นส่วนย่อย ๆ — แต่ละ component ควรรับผิดชอบหนึ่งหน่วยตรรกะของอินเทอร์เฟซ
คำถามที่พบบ่อย
ได้ ผ่าน Babel-ปลั๊กอิน @babel/plugin-transform-react-jsx คุณสามารถตั้งค่า JSX สำหรับไลบรารีใดก็ได้ SolidJS, Preact, Nerv และเฟรมเวิร์กอื่น ๆ ใช้ JSX พร้อม runtime ของตัวเอง สำหรับความเป็นอิสระโดยสมบูรณ์คุณสามารถระบุ pragma เป็นฟังก์ชันของคุณเองได้
class เป็นคำสงวนใน JavaScript JSX ถูกแปลงเป็น createElement ซึ่งอาร์กิวเมนต์ที่สองคือออบเจกต์ของ props คุณสมบัติ class ขัดแย้งกับไวยากรณ์ class ของ ES6 ดังนั้น React จึงเลือก className
ใช้ เทมเพลตสตริง หรือไลบรารี classnames: className={`"btn ${isActive ? 'active' : ''}"`} สำหรับตรรกะที่ซับซ้อนให้ติดตั้ง npm package clsx หรือ classnames ซึ่งรับออบเจกต์และอาร์เรย์
JSX รองรับเฉพาะ นิพจน์ ไม่ใช่คำสั่ง สำหรับเงื่อนไขให้ใช้ตัวดำเนินการ ternary, logical AND (&&) หรือ logical OR (||) สำหรับตรรกะที่ซับซ้อนให้แยกเงื่อนไขออกไปใส่ตัวแปรหรือฟังก์ชัน
แอตทริบิวต์ key ช่วยให้ React ระบุ element ของรายการเมื่อเรนเดอร์ซ้ำ หากไม่มี key React จะวาดรายการใหม่ทั้งหมดเมื่อมีการเปลี่ยนแปลง ด้วย key ที่ไม่ซ้ำกัน React จะนำ DOM node กลับมาใช้ใหม่และจัดลำดับ element แทนที่จะสร้างใหม่
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม