编写组件

组件是 Vitarx 应用的基本构建单元。本页介绍如何定义和使用函数组件。

函数组件

Vitarx 使用函数组件模型——组件就是一个接收 props、返回 JSX 视图的函数:

tsx
import { createApp } from 'vitarx'

// 定义一个简单的组件
function Greeting(props: { name: string }) {
  return <div>Hello, {props.name}!</div>
}

function App() {
  return (
    <div>
      <Greeting name="Vitarx" />
      <Greeting name="World" />
    </div>
  )
}

createApp(App).mount('#app')

提示

与 React 不同,Vitarx 的组件函数只执行一次。当响应式数据变化时,框架会精确更新对应的 DOM 节点,不会重新执行组件函数。这意味着你不需要担心组件函数被反复调用带来的性能问题。

接收 props

组件通过函数参数接收 props,直接通过属性名访问:

tsx
function UserCard(props: { name: string; age: number }) {
  return (
    <div>
      <p>姓名: {props.name}</p>
      <p>年龄: {props.age}</p>
    </div>
  )
}

// 使用时传入 props
;<UserCard name="张三" age={25} />

children 传递

当组件需要接收子内容时,可以通过 props.children 获取:

tsx
function Card(props: { title: string; children: any }) {
  return (
    <div style={{ border: '1px solid #ccc', padding: '16px' }}>
      <h2>{props.title}</h2>
      <div>{props.children}</div>
    </div>
  )
}

function App() {
  return (
    <Card title="用户信息">
      <p>这是卡片的内容</p>
      <p>可以放任意子元素</p>
    </Card>
  )
}

使用 builder(纯函数视图)

builder 是一个纯函数视图构建器。它将一个函数标记为视图构建函数,在视图构建阶段被调用。使用 builder 包裹的函数不具有独立的组件实例和生命周期,它会随父组件创建而创建、父组件销毁而销毁。

基本用法

tsx
import { builder, createApp } from 'vitarx'

// 创建纯函数视图
const Greeting = builder((props: { name: string }) => {
  return <div>Hello, {props.name}!</div>
})

function App() {
  return <Greeting name="Vitarx" />
}

createApp(App).mount('#app')

指定显示名称

第二个参数可以指定组件在开发工具中的显示名称:

tsx
const MyButton = builder((props: { onClick: () => void; children: any }) => {
  return <button onClick={props.onClick}>{props.children}</button>
}, 'MyButton')

builder vs 普通函数组件

特性builder 视图普通函数组件
组件实例
生命周期钩子不建议使用(卸载时不触发)完整支持
依赖注入支持支持
性能开销更小(无实例开销)相对较大
适用场景纯展示型 UI、可复用组件需要状态管理、副作用处理

适用场景

推荐使用 builder 的情况:

  • 创建纯展示型的 UI 组件(如 Button、Card、Icon 等)
  • 不需要生命周期钩子的简单组件
  • 追求最小性能开销的高频渲染组件

不适用场景

不推荐使用 builder 的情况:

  • 需要使用 onMountedonDispose 等生命周期钩子
  • 需要管理复杂状态或副作用,如 watchComputed

WARNING

虽然 builder 技术上可以调用生命周期钩子(会注册到父组件作用域),但当仅卸载该视图时(如通过条件渲染),这些钩子不会被触发,只有父组件整体销毁时才会执行。因此,builder 应仅用于纯展示型组件。

组件组合

多个组件可以自由组合使用,构建复杂的界面:

tsx
import { builder, createApp, ref } from 'vitarx'

// 头部组件
const Header = builder((props: { title: string }) => {
  return (
    <header>
      <h1>{props.title}</h1>
    </header>
  )
})

// 列表项组件
const Item = builder((props: { text: string }) => {
  return <li>{props.text}</li>
})

// 页面组件
function App() {
  const items = ['学习 Vitarx', '编写组件', '构建应用']

  return (
    <div>
      <Header title="我的待办" />
      <ul>
        {items.map((text) => (
          <Item text={text} />
        ))}
      </ul>
    </div>
  )
}

createApp(App).mount('#app')

完整示例

下面是一个包含响应式状态和事件处理的完整组件示例:

tsx
import { builder, createApp, ref } from 'vitarx'

// 可复用的按钮组件
const Button = builder((props: { onClick: () => void; children: any }) => {
  return (
    <button
      onClick={props.onClick}
      style={{
        padding: '8px 16px',
        cursor: 'pointer',
        marginRight: '8px'
      }}
    >
      {props.children}
    </button>
  )
})

// 根组件
function App() {
  const count = ref(0)

  const increment = () => {
    count.value++
  }
  const decrement = () => {
    count.value--
  }
  const reset = () => {
    count.value = 0
  }

  return (
    <div style={{ textAlign: 'center', marginTop: '40px' }}>
      <h1>计数器</h1>
      <p style={{ fontSize: '48px' }}>{count}</p>
      <div>
        <Button onClick={decrement}>-1</Button>
        <Button onClick={reset}>重置</Button>
        <Button onClick={increment}>+1</Button>
      </div>
    </div>
  )
}

createApp(App).mount('#app')

下一步

  • 模板语法 — 学习 JSX 中的表达式、样式绑定和事件处理