JSX là phần mở rộng cú pháp giống XML dành cho JavaScript, được nhóm React phát triển để mô tả giao diện người dùng một cách khai báo. Không giống các công cụ template, JSX biên dịch thành các lời gọi hàm JavaScript thông thường, mang lại sức mạnh đầy đủ của ngôn ngữ trong đánh dấu. Theo React, 2024, JSX được hỗ trợ trong tất cả các framework và thư viện React hiện đại.
Các điểm chính
JSX (JavaScript XML) là phần mở rộng cú pháp cho JavaScript, được giới thiệu lần đầu trong React vào năm 2013. Nó cho phép nhà phát triển mô tả cấu trúc giao diện người dùng theo phong cách khai báo, kết hợp đánh dấu và logic trong một tập tin.
Trước JSX, các nhà phát triển sử dụng template chuỗi (Mustache, Handlebars) hoặc tạo phần tử DOM thủ công qua document.createElement. Cả hai cách tiếp cận đều có nhược điểm: template chuỗi không được kiểm tra kiểu, và tạo DOM thủ công rườm rà và dễ xảy ra lỗi.
React đã chọn một con đường khác — nhúng đánh dấu trực tiếp vào mã. Quyết định này đã gây ra tranh luận trong cộng đồng, nhưng theo thời gian nó đã trở thành tiêu chuẩn. Ngày nay, JSX không chỉ được sử dụng trong React mà còn trong SolidJS, Preact, Qwik và thậm chí trong Vue thông qua các plugin.
Phần tử JSX trông giống thẻ HTML nhưng được nhúng trực tiếp vào JavaScript. Mỗi phần tử được biên dịch thành lời gọi React.createElement với ba đối số: loại phần tử, đối tượng props và các phần tử con. Một phần tử gốc là quy tắc bắt buộc: biểu thức JSX phải trả về một phần tử cha hoặc fragment.
import React from 'react';
// JSX cơ bản - một phần tử gốc
const element = (
<div className="app">
<h1>Hello, World!</h1>
<p>Welcome to React</p>
</div>
);
Khi không cần nút DOM bổ sung, hãy sử dụng React.Fragment hoặc cú pháp rút gọn <></>. Fragment không tạo phần tử DOM thật mà chỉ nhóm các nút con. Điều này đặc biệt hữu ích trong bảng và danh sách, nơi một wrapper bổ sung sẽ phá vỡ cấu trúc HTML.
function Columns() {
return (
<>
<td>First</td>
<td>Second</td>
</>
);
}
Bất kỳ biểu thức JavaScript nào cũng được nhúng trong JSX qua dấu ngoặc nhọn { }. Bên trong, bạn có thể sử dụng biến, lời gọi hàm, toán tử ba ngôi và phương thức mảng. Kết xuất có điều kiện được thực hiện qua toán tử ba ngôi hoặc AND logic.
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>
);
}
Một giới hạn chính — chỉ có biểu thức mới có thể được sử dụng bên trong dấu ngoặc nhọn, không phải câu lệnh. Bạn không thể sử dụng if/else, for, switch — chỉ toán tử ba ngôi, toán tử logic và phương thức mảng. Lời gọi hàm được phép, bao gồm cả hàm mũi tên.
JSX trông giống HTML nhưng có những khác biệt cơ bản. Thuộc tính được viết bằng camelCase — className thay vì class, onClick thay vì onclick, tabIndex thay vì tabindex. Điều này là do JSX biên dịch thành JavaScript, nơi dấu gạch nối trong tên thuộc tính không được phép.
| HTML | JSX | Lý do |
|---|---|---|
class="wrapper" | className="wrapper" | class là từ khóa dành riêng trong JS |
onclick="handle()" | onClick={handle} | camelCase cho thuộc tính JS |
style="color: red" | style={{ color: ‘red’ }} | Đối tượng style thay vì chuỗi |
<input disabled> | <input disabled={true}> | Thuộc tính boolean tường minh |
Một khác biệt quan trọng nữa — thẻ tự đóng. Trong HTML, một số thẻ (br, hr, input) có thể không đóng. Trong JSX, tất cả các thẻ tự đóng phải có dấu gạch chéo đóng: <br />, <input />. Tên thuộc tính SVG cũng được chuyển thành camelCase: strokeWidth, fillOpacity.
Trình duyệt không hiểu JSX trực tiếp. Trước khi thực thi, mã JSX đi qua một trình biên dịch — Babel, TypeScript hoặc esbuild. Mỗi phần tử JSX được biến thành lời gọi createElement hoặc jsx/runtime.
// JSX gốc
const element = <h1 className="title">Hello</h1>;
// Sau khi biên dịch
const element = React.createElement(
'h1',
{ className: 'title' },
'Hello'
);
Với React 17, một phép biến đổi JSX mới đã xuất hiện không yêu cầu import React. Import tự động jsx-runtime cho phép viết component mà không cần import React from ‘react’. Babel và TypeScript hỗ trợ phép biến đổi này thông qua cấu hình {"runtime": "automatic"}.
Mặc dù JSX gắn liền với React, các framework khác cũng sử dụng nó. SolidJS sử dụng JSX với biên dịch thành các nút DOM thật, không cần Virtual DOM. Preact là một thay thế nhẹ cho React với hỗ trợ JSX đầy đủ. Vue hỗ trợ JSX qua @vue/babel-plugin-jsx, mặc dù template chính của nó là template.
Một khác biệt chính là ngữ nghĩa biểu thức. Trong React, các biểu thức JSX được thực thi ở mỗi lần kết xuất. Trong SolidJS, JSX biên dịch một lần và các bản cập nhật được theo dõi qua tín hiệu. Điều này ảnh hưởng đến hiệu suất: SolidJS không cần Virtual DOM và điều phối, làm cho việc kết xuất JSX nhanh hơn.
Trong TypeScript, JSX được hỗ trợ ở cấp độ ngôn ngữ. Các tập tin có phần mở rộng .tsx tự động xử lý cú pháp JSX. Đánh kiểu props là một trong những lợi thế chính của TypeScript + JSX: IDE gợi ý các thuộc tính mong đợi, kiểu callback và tính bắt buộc của props.
Viết mã JSX dễ đọc và dễ bảo trì. Trích xuất các biểu thức phức tạp vào các biến hoặc hàm riêng — điều này cải thiện khả năng đọc và cho phép kiểm tra logic riêng biệt với đánh dấu. Sử dụng fragment thay vì các wrapper div không cần thiết.
Quy tắc thụt lề — duy trì thụt lề cho các phần tử lồng nhau. Mỗi cấp độ lồng nhau là một cấp độ thụt lề. Thẻ đóng phải ở cùng cấp với thẻ mở. Đối với các thuộc tính dài, hãy sử dụng xuống dòng.
Tránh hàm mũi tên nội tuyến trong props nếu chúng được tạo ra ở mỗi lần kết xuất. Trong trường hợp này, hãy bọc callback trong useCallback hoặc di chuyển hàm ra ngoài component. Chia các component phức tạp thành những component nhỏ hơn — mỗi component nên xử lý một đơn vị logic của giao diện.
Câu hỏi thường gặp
Có, thông qua plugin Babel @babel/plugin-transform-react-jsx, JSX có thể được cấu hình cho bất kỳ thư viện nào. SolidJS, Preact, Nerv và các framework khác sử dụng JSX với runtime riêng. Để hoàn toàn độc lập, bạn có thể đặt pragma thành hàm của riêng mình.
class là từ khóa dành riêng trong JavaScript. JSX biên dịch thành createElement, nơi đối số thứ hai truyền đối tượng props. Thuộc tính class xung đột với cú pháp lớp ES6, vì vậy React đã chọn className.
Sử dụng template string hoặc thư viện classnames: className={`btn ${isActive ? ‘active’ : ‘’}`}. Đối với logic phức tạp, hãy cài đặt gói npm clsx hoặc classnames, chấp nhận cả đối tượng và mảng.
JSX chỉ hỗ trợ biểu thức, không phải câu lệnh. Đối với điều kiện, hãy sử dụng toán tử ba ngôi, AND logic (&&) hoặc OR logic (||). Đối với logic phức tạp, hãy trích xuất điều kiện vào một biến hoặc hàm riêng.
Thuộc tính key giúp React xác định các phần tử danh sách khi kết xuất lại. Không có key, React kết xuất lại toàn bộ danh sách khi có thay đổi. Với key duy nhất, React tái sử dụng các nút DOM và sắp xếp lại các phần tử thay vì tạo lại chúng.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm