JSX语法 #
一、JSX 简介 #
1.1 什么是 JSX #
JSX 是 JavaScript 的语法扩展,允许在 JavaScript 中编写类似 HTML 的代码。
jsx
const element = <h1>Hello, Preact!</h1>;
1.2 JSX 与 HTML 的区别 #
| 方面 | JSX | HTML |
|---|---|---|
| class 属性 | className 或 class |
class |
| for 属性 | htmlFor |
for |
| 内联样式 | 对象形式 | 字符串 |
| 事件 | 驼峰命名 | 小写 |
| 注释 | {/* */} |
<!-- --> |
1.3 Preact 中的 JSX #
Preact 支持两种写法:
jsx
// 标准写法(与 React 一致)
<div className="container">Content</div>
// Preact 简化写法
<div class="container">Content</div>
二、基本语法 #
2.1 元素 #
jsx
// 单标签
<input type="text" />
<img src="image.jpg" alt="image" />
// 双标签
<div>Content</div>
<h1>Title</h1>
<p>Paragraph</p>
2.2 嵌套 #
jsx
<div>
<h1>Title</h1>
<p>Paragraph</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</div>
2.3 Fragment #
jsx
import { Fragment } from 'preact';
// 使用 Fragment 包裹多个元素
function List() {
return (
<Fragment>
<li>Item 1</li>
<li>Item 2</li>
</Fragment>
);
}
// 简写形式
function List() {
return (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
);
}
三、表达式 #
3.1 插入变量 #
jsx
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
const user = 'Alice';
const element = <p>Welcome, {user}</p>;
3.2 表达式计算 #
jsx
function Calculator({ a, b }) {
return (
<div>
<p>Sum: {a + b}</p>
<p>Product: {a * b}</p>
<p>Random: {Math.random()}</p>
</div>
);
}
3.3 函数调用 #
jsx
function formatName(user) {
return `${user.firstName} ${user.lastName}`;
}
function Greeting({ user }) {
return <h1>Hello, {formatName(user)}!</h1>;
}
3.4 三元表达式 #
jsx
function Status({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? 'Welcome back!' : 'Please log in'}
</div>
);
}
四、属性 #
4.1 字符串属性 #
jsx
<div class="container">
<img src="image.jpg" alt="description" />
<a href="https://example.com">Link</a>
</div>
4.2 动态属性 #
jsx
function Image({ src, alt }) {
return <img src={src} alt={alt} />;
}
function Link({ url, text }) {
return <a href={url}>{text}</a>;
}
4.3 布尔属性 #
jsx
// 以下写法等价
<input disabled />
<input disabled={true} />
// 不禁用
<input disabled={false} />
4.4 展开属性 #
jsx
function Button(props) {
return <button {...props}>Click me</button>;
}
// 使用
<Button
className="btn"
onClick={() => console.log('clicked')}
disabled={false}
/>
4.5 class 与 className #
jsx
// Preact 支持两种写法
<div class="container">Content</div>
<div className="container">Content</div>
// 动态 class
function Box({ isActive }) {
return (
<div class={`box ${isActive ? 'active' : ''}`}>
Content
</div>
);
}
// 多个 class
<div class="box primary large">Content</div>
五、样式 #
5.1 内联样式 #
jsx
function StyledBox() {
const style = {
backgroundColor: 'lightblue',
padding: '20px',
borderRadius: '8px'
};
return <div style={style}>Styled Content</div>;
}
// 直接写
function InlineStyle() {
return (
<div style={{
color: 'red',
fontSize: '16px',
marginTop: '10px'
}}>
Styled Text
</div>
);
}
5.2 样式属性命名 #
jsx
// CSS 属性转为驼峰命名
const style = {
backgroundColor: 'blue', // background-color
fontSize: '16px', // font-size
marginTop: '10px', // margin-top
borderRadius: '8px', // border-radius
textAlign: 'center' // text-align
};
5.3 CSS Modules #
jsx
import styles from './Button.module.css';
function Button() {
return (
<button class={styles.button}>
Click me
</button>
);
}
六、条件渲染 #
6.1 if 语句 #
jsx
function Greeting({ user }) {
if (user) {
return <h1>Hello, {user.name}!</h1>;
}
return <h1>Please log in</h1>;
}
6.2 三元运算符 #
jsx
function Status({ isLoading }) {
return (
<div>
{isLoading ? <span>Loading...</span> : <span>Content loaded</span>}
</div>
);
}
6.3 逻辑与 (&&) #
jsx
function Notifications({ count }) {
return (
<div>
{count > 0 && <span>{count} unread messages</span>}
</div>
);
}
6.4 立即执行函数 #
jsx
function ComplexCondition({ status }) {
return (
<div>
{(() => {
switch (status) {
case 'loading':
return <span>Loading...</span>;
case 'success':
return <span>Success!</span>;
case 'error':
return <span>Error occurred</span>;
default:
return null;
}
})()}
</div>
);
}
6.5 提前返回 #
jsx
function UserList({ users }) {
if (!users || users.length === 0) {
return <p>No users found</p>;
}
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
七、列表渲染 #
7.1 map 方法 #
jsx
function NumberList({ numbers }) {
return (
<ul>
{numbers.map((number, index) => (
<li key={index}>{number}</li>
))}
</ul>
);
}
7.2 key 属性 #
jsx
// 使用唯一 ID 作为 key
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
// 不要使用索引作为 key(除非列表是静态的)
function StaticList({ items }) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
7.3 复杂列表 #
jsx
function ProductList({ products }) {
return (
<div class="product-grid">
{products.map(product => (
<div key={product.id} class="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>${product.price}</p>
<button onClick={() => addToCart(product)}>
Add to Cart
</button>
</div>
))}
</div>
);
}
7.4 嵌套列表 #
jsx
function CategoryList({ categories }) {
return (
<ul>
{categories.map(category => (
<li key={category.id}>
<h3>{category.name}</h3>
<ul>
{category.items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</li>
))}
</ul>
);
}
八、事件处理 #
8.1 基本事件 #
jsx
function Button() {
const handleClick = () => {
console.log('Button clicked');
};
return <button onClick={handleClick}>Click me</button>;
}
8.2 事件对象 #
jsx
function Form() {
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form submitted');
};
const handleInput = (e) => {
console.log(e.target.value);
};
return (
<form onSubmit={handleSubmit}>
<input onInput={handleInput} />
<button type="submit">Submit</button>
</form>
);
}
8.3 传递参数 #
jsx
function ItemList({ items }) {
const handleDelete = (id) => {
console.log('Delete item:', id);
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
<button onClick={() => handleDelete(item.id)}>
Delete
</button>
</li>
))}
</ul>
);
}
8.4 事件命名 #
jsx
// Preact 使用驼峰命名
<div onClick={handleClick} />
<input onInput={handleInput} />
<form onSubmit={handleSubmit} />
<input onKeyDown={handleKeyDown} />
<input onKeyUp={handleKeyUp} />
<div onMouseEnter={handleMouseEnter} />
<div onMouseLeave={handleMouseLeave} />
九、注释 #
9.1 JSX 注释 #
jsx
function Component() {
return (
<div>
{/* 这是 JSX 注释 */}
<h1>Title</h1>
{/*
多行注释
第二行
*/}
<p>Content</p>
</div>
);
}
9.2 条件注释 #
jsx
function DebugInfo({ show }) {
return (
<div>
{show && (
<>
{/* 调试信息 */}
<pre>{JSON.stringify(data, null, 2)}</pre>
</>
)}
</div>
);
}
十、特殊处理 #
10.1 HTML 转义 #
jsx
// JSX 自动转义
const content = '<script>alert("xss")</script>';
<div>{content}</div> // 安全,显示为文本
// 插入原始 HTML(谨慎使用)
const html = '<strong>Bold</strong>';
<div dangerouslySetInnerHTML={{ __html: html }} />
10.2 空白处理 #
jsx
// JSX 会忽略空白
<div>
<span>Hello</span>
<span>World</span>
</div>
// 输出: HelloWorld
// 添加空格
<div>
<span>Hello</span>
{' '}
<span>World</span>
</div>
// 输出: Hello World
// 使用模板字符串
<div>{`Hello ${name}`}</div>
10.3 空值处理 #
jsx
// null 和 undefined 不会渲染
<div>{null}</div> // 不显示
<div>{undefined}</div> // 不显示
<div>{false}</div> // 不显示
<div>{true}</div> // 不显示
// 0 会显示
<div>{0}</div> // 显示 0
// 安全显示
<div>{value || 'Default'}</div>
十一、JSX 编译 #
11.1 编译结果 #
jsx
// JSX
<div class="container">
<h1>Hello</h1>
</div>
// 编译后
h('div', { class: 'container' },
h('h1', null, 'Hello')
)
11.2 配置 JSX #
javascript
// vite.config.js
import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
export default defineConfig({
plugins: [preact()]
});
十二、最佳实践 #
12.1 代码组织 #
jsx
// 好的做法:组件拆分
function UserCard({ user }) {
return (
<div class="user-card">
<Avatar url={user.avatar} />
<UserInfo name={user.name} email={user.email} />
<UserActions userId={user.id} />
</div>
);
}
12.2 条件渲染选择 #
jsx
// 简单条件:三元运算符
{isLoading ? <Spinner /> : <Content />}
// 仅显示:逻辑与
{showWarning && <Warning />}
// 复杂条件:提前返回或提取函数
function getStatusMessage(status) {
switch (status) {
case 'loading': return 'Loading...';
case 'success': return 'Success!';
case 'error': return 'Error!';
default: return null;
}
}
十三、总结 #
| 要点 | 说明 |
|---|---|
| 表达式 | 使用 {} 插入 JavaScript |
| 属性 | 使用 class 或 className |
| 条件渲染 | 三元运算符、&&、提前返回 |
| 列表渲染 | 使用 map,需要 key |
| 事件 | 驼峰命名,传递函数 |
| 注释 | {/* */} 格式 |
最后更新:2026-03-28