模板语法

Vitarx 使用 JSX 作为模板语法。本页介绍 JSX 中常用的绑定方式,包括表达式、样式、事件和属性绑定。

JSX 表达式

在 JSX 中,使用 {} 包裹 JavaScript 表达式,表达式的结果会被渲染到页面上:

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const name = ref('Vitarx')
  const version = 4

  return (
    <div>
      <p>框架名称: {name}</p>
      <p>版本号: {version}</p>
      <p>计算结果: {1 + 1}</p>
    </div>
  )
}

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

你可以放入任何 JavaScript 表达式,包括函数调用、三元运算等:

tsx
<p>{name.value.length > 5 ? '名称较长' : '名称较短'}</p>

INFO

在模板中使用 ref 时,不需要写 .value,框架会自动解包。例如 {name} 等同于 {name.value}

class 绑定

字符串绑定

直接传入字符串:

tsx
<div class="container">内容</div>

对象绑定

使用对象动态控制类名,属性名为类名,值为布尔值——值为 true 时添加该类名:

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const isActive = ref(true)

  return <div class={{ active: isActive, disabled: false }}>内容</div>
}

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

渲染结果:<div class="active">内容</div>

数组绑定

使用数组组合多个类名:

tsx
<div class={['base', isActive.value ? 'active' : '']}>内容</div>

混合使用

字符串、对象、数组可以混合使用:

tsx
<div class={['container', { active: isActive, 'no-border': true }]}>内容</div>

style 绑定

对象绑定

使用对象设置内联样式,属性名使用驼峰命名:

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const color = ref('red')

  return <div style={{ color, fontSize: '16px', marginTop: '20px' }}>彩色文字</div>
}

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

INFO

  • CSS 属性名使用驼峰命名:font-sizefontSizebackground-colorbackgroundColor
  • 数值类型的属性值会自动添加 px 后缀(如 marginTop: 20 等同于 marginTop: '20px'

字符串绑定

也可以直接传入 CSS 字符串:

tsx
<div style="color: red; font-size: 16px;">文字</div>

事件处理

基本用法

事件使用驼峰命名(如 onClickonInput),传入事件处理函数:

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const count = ref(0)

  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => count.value++}>点击 +1</button>
    </div>
  )
}

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

事件对象

事件处理函数会接收原生事件对象作为参数:

tsx
function App() {
  const handleInput = (e: Event) => {
    const target = e.target as HTMLInputElement
    console.log('输入内容:', target.value)
  }

  return <input onInput={handleInput} placeholder="请输入" />
}

常用事件

事件名说明
onClick点击
onInput输入
onChange变更
onSubmit提交
onKeyDown按键按下
onKeyUp按键抬起
onMouseEnter鼠标移入
onMouseLeave鼠标移出
onFocus获得焦点
onBlur失去焦点

展开绑定

Vitarx 支持两种方式将对象的所有属性绑定到元素上:v-bind 和 JSX 扩展语法 {...props}注意这两种语法互斥,只能使用其中一种。

方式一:v-bind(推荐用于排除属性)

对象形式

将整个对象的属性展开到元素上:

tsx
import { createApp } from 'vitarx'

function App() {
  const attrs = {
    id: 'my-input',
    placeholder: '请输入',
    maxLength: 100
  }

  return <input v-bind={attrs} />
}

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

数组形式(排除属性)

如果你想在展开时排除某些属性,可以使用数组形式,第二个元素为要排除的属性名列表:

tsx
const attrs = {
  id: 'my-input',
  placeholder: '请输入',
  class: 'input-field',
  style: { color: 'red' }
}

// 排除 class 和 style
<input v-bind={[attrs, ['class', 'style']]} />

方式二:JSX 扩展语法 {…props}(推荐用于组件)

使用标准的 JSX 扩展语法展开对象:

tsx
import { createApp } from 'vitarx'

function Button(props: { [key: string]: any }) {
  return (
    <button type="button" {...props}>
      Click me
    </button>
  )
}

function App() {
  const buttonProps = {
    id: 'my-button',
    disabled: false,
    style: { padding: '10px 20px' }
  }

  return <Button {...buttonProps} />
}

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

两种方式对比

特性v-bind{...props}
排除属性支持 v-bind={[obj, ['exclude']]}不支持
JSX 标准非标准标准语法
适用场景需要排除特定属性组件透传 props
互斥与 {…props} 互斥与 v-bind 互斥

WARNING

v-bind{...props}不能同时使用在同一个元素或组件上,否则会导致编译错误。选择一种方式即可。

INFO

v-bind 会智能合并 classstyle 属性,不会覆盖已有的值,而是合并在一起。

ref 绑定

绑定 DOM 元素

使用 ref 属性获取 DOM 元素的引用:

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

function App() {
  const inputRef = ref<HTMLInputElement | null>(null)

  onMounted(() => {
    // 挂载后自动聚焦
    inputRef.value?.focus()
  })

  return <input ref={inputRef} placeholder="自动聚焦" />
}

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

ref 属性也可以传入一个回调函数,在元素挂载时调用:

tsx
function App() {
  const setInput = (el: HTMLInputElement) => {
    el.focus()
  }

  return <input ref={setInput} placeholder="自动聚焦" />
}

绑定组件实例

ref 也可以绑定到组件上,获取组件通过 defineExpose 暴露的成员:

tsx
import { ref, createApp, defineExpose, onMounted } from 'vitarx'

// 子组件:通过 defineExpose 暴露方法
function Child() {
  const count = ref(0)

  const increment = () => {
    count.value++
  }

  const reset = () => {
    count.value = 0
  }

  // 暴露给父组件的成员
  defineExpose({ increment, reset, count })

  return <div>子组件计数: {count}</div>
}

// 父组件:通过 ref 调用子组件方法
function App() {
  const childRef = ref<any>(null)

  onMounted(() => {
    // 调用子组件暴露的方法
    childRef.value?.increment()
  })

  return (
    <div>
      <Child ref={childRef} />
      <button onClick={() => childRef.value?.reset()}>重置子组件</button>
    </div>
  )
}

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

提示

组件默认不暴露任何成员。只有通过 defineExpose 显式声明的属性和方法,才能被父组件通过 ref 访问。更多用法请参考组件引用

完整示例

下面是一个综合运用各种绑定的示例:

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const text = ref('')
  const isFocused = ref(false)
  const inputRef = ref<HTMLInputElement | null>(null)

  const handleInput = (e: Event) => {
    text.value = (e.target as HTMLInputElement).value
  }

  return (
    <div style={{ maxWidth: '400px', margin: '40px auto' }}>
      <h2 style={{ textAlign: 'center' }}>模板语法示例</h2>

      <input
        ref={inputRef}
        value={text}
        onInput={handleInput}
        onFocus={() => {
          isFocused.value = true
        }}
        onBlur={() => {
          isFocused.value = false
        }}
        placeholder="请输入文字"
        class={{
          'input-base': true,
          'input-focused': isFocused
        }}
        style={{
          width: '100%',
          padding: '8px 12px',
          border: isFocused.value ? '2px solid #4a90d9' : '2px solid #ccc',
          borderRadius: '4px',
          outline: 'none',
          fontSize: '14px',
          boxSizing: 'border-box'
        }}
      />

      <p style={{ marginTop: '12px', color: '#666' }}>
        你输入了: {text.value.length > 0 ? text : '(空)'}
      </p>

      <p style={{ marginTop: '8px', fontSize: '14px', color: '#999' }}>
        字符数: {text.value.length}
      </p>
    </div>
  )
}

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

下一步