JSX语法 #

一、JSX 简介 #

1.1 什么是 JSX #

JSX 是 JavaScript 的语法扩展,允许在 JavaScript 中编写类似 HTML 的代码。

jsx
const element = <h1>Hello, Preact!</h1>;

1.2 JSX 与 HTML 的区别 #

方面 JSX HTML
class 属性 classNameclass 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
属性 使用 classclassName
条件渲染 三元运算符、&&、提前返回
列表渲染 使用 map,需要 key
事件 驼峰命名,传递函数
注释 {/* */} 格式
最后更新:2026-03-28