内置指令

Vitarx 提供了一组内置指令,用于处理常见的 DOM 操作场景。这些指令可以直接在 JSX 中使用,无需额外注册。

v-show

v-show 通过切换 display: none 来控制元素的显示与隐藏。与 v-if 不同,v-show 不会销毁和重建 DOM 元素,只是简单地切换 CSS 属性。

基本用法

tsx
import { ref, createApp } from 'vitarx'

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

  return (
    <div>
      <button
        onClick={() => {
          visible.value = !visible.value
        }}
      >
        {visible.value ? '隐藏' : '显示'}
      </button>
      <div v-show={visible}>这段内容可以显示或隐藏</div>
    </div>
  )
}

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

v-show 与 v-if 的区别

特性v-showv-if
切换方式CSS display: none销毁和重建 DOM
初始渲染始终渲染 DOM条件为假时不渲染
切换开销低(仅切换样式)高(重建 DOM)
适用场景频繁切换显示/隐藏条件很少改变

简单来说:需要频繁切换用 v-show,条件基本不变用 v-if

v-html

v-html 用于将 HTML 字符串渲染到元素内部,相当于设置 innerHTML

基本用法

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const content = ref('<strong>加粗文本</strong> 和 <em>斜体文本</em>')

  return (
    <div>
      <div v-html={content} />
      <p>原始 HTML:{content}</p>
    </div>
  )
}

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

WARNING

v-html 会直接将字符串作为 HTML 插入页面,存在 XSS 攻击风险。不要将用户输入的内容直接传给 v-html,务必先进行安全过滤。

INFO

v-html 应该用在没有子节点的元素上。如果元素已经有子节点,框架会发出警告。

v-text

v-text 用于设置元素的文本内容,相当于设置 textContent。与 {} 表达式不同,v-text 会替换元素内的所有内容。

基本用法

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const message = ref('Hello Vitarx')

  return (
    <div>
      <p v-text={message} />
      <button
        onClick={() => {
          message.value = '你好,Vitarx'
        }}
      >
        切换文本
      </button>
    </div>
  )
}

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

INFO

v-text 应该用在没有子节点的元素上。如果元素已经有子节点,框架会发出警告。大多数情况下,使用 {} 表达式更直观:<p>{message}</p>

v-if / v-else-if / v-else

v-ifv-else-ifv-else 是条件渲染指令,根据条件决定是否渲染 DOM 元素。条件为假时,元素不会被创建(页面中只会留下一个注释占位符)。

基本用法

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const type = ref<'admin' | 'user' | 'guest'>('guest')

  return (
    <div>
      <div v-if={type.value === 'admin'}>管理员面板</div>
      <div v-else-if={type.value === 'user'}>用户中心</div>
      <div v-else>访客页面</div>

      <div style={{ marginTop: '12px' }}>
        <button
          onClick={() => {
            type.value = 'admin'
          }}
        >
          管理员
        </button>
        <button
          onClick={() => {
            type.value = 'user'
          }}
        >
          用户
        </button>
        <button
          onClick={() => {
            type.value = 'guest'
          }}
        >
          访客
        </button>
      </div>
    </div>
  )
}

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

v-if 与 v-else 配合

v-ifv-else 必须紧邻使用,中间不能插入其他元素:

tsx
// ✅ 正确:v-if 和 v-else 紧邻
<div v-if={show}>显示内容</div>
<div v-else>隐藏内容</div>

// ❌ 错误:中间插入了其他元素,v-else 无法匹配 v-if
<div v-if={show}>显示内容</div>
<p>其他元素</p>
<div v-else>隐藏内容</div>

INFO

v-if / v-else-if / v-else 是编译器指令,由 @vitarx/plugin-vite 在编译阶段处理。条件为假的分支不会创建 DOM,切换时会销毁和重建 DOM 元素。

v-bind

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
<input id="my-input" placeholder="请输入" maxLength={100} />

数组形式(排除属性)

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

tsx
import { createApp } from 'vitarx'

function MyInput(props: { class?: string; value?: string; onInput?: (e: Event) => void }) {
  return (
    <input
      v-bind={[props, ['onInput']]} // 透传 props,但排除 onInput
      onInput={(e) => {
        /* 自定义处理逻辑 */
      }}
    />
  )
}

class 和 style 的合并行为

v-bind 会智能合并 classstyle 属性,不会覆盖元素上已有的值:

tsx
// class 和 style 会合并,而不是覆盖
<div class="base" style={{ color: 'red' }} v-bind={{ class: 'extra', style: { fontSize: '16px' } }}>
  内容
</div>
// 最终结果:class="base extra",style 同时包含 color 和 fontSize

WARNING

v-bind 不能用于绑定全局属性(如 refchildren)和其他 v- 开头的属性,这些属性会被自动忽略。

完整示例

下面是一个综合运用各种内置指令的完整示例:

tsx
import { ref, createApp } from 'vitarx'

function App() {
  const visible = ref(true)
  const type = ref<'admin' | 'user' | 'guest'>('guest')
  const richContent = ref('<em>这是一段富文本内容</em>')
  const plainText = ref('纯文本内容')
  const attrs = {
    title: '属性透传示例',
    'data-role': 'container'
  }

  return (
    <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
      <h2>内置指令示例</h2>

      {/* v-show:控制显示/隐藏 */}
      <section style={{ marginBottom: '20px' }}>
        <h3>v-show</h3>
        <button
          onClick={() => {
            visible.value = !visible.value
          }}
        >
          {visible.value ? '隐藏' : '显示'}
        </button>
        <p v-show={visible} style={{ marginTop: '8px' }}>
          这段内容通过 v-show 控制显示与隐藏
        </p>
      </section>

      {/* v-if / v-else-if / v-else:条件渲染 */}
      <section style={{ marginBottom: '20px' }}>
        <h3>v-if / v-else-if / v-else</h3>
        <div v-if={type.value === 'admin'} style={{ color: '#e74c3c' }}>
          管理员面板:拥有所有权限
        </div>
        <div v-else-if={type.value === 'user'} style={{ color: '#3498db' }}>
          用户中心:查看个人信息
        </div>
        <div v-else style={{ color: '#95a5a6' }}>
          访客页面:请先登录
        </div>
        <div style={{ marginTop: '8px' }}>
          <button
            onClick={() => {
              type.value = 'admin'
            }}
          >
            管理员
          </button>
          <button
            onClick={() => {
              type.value = 'user'
            }}
          >
            用户
          </button>
          <button
            onClick={() => {
              type.value = 'guest'
            }}
          >
            访客
          </button>
        </div>
      </section>

      {/* v-html:渲染 HTML */}
      <section style={{ marginBottom: '20px' }}>
        <h3>v-html</h3>
        <div v-html={richContent} style={{ border: '1px solid #ddd', padding: '8px' }} />
      </section>

      {/* v-text:设置文本 */}
      <section style={{ marginBottom: '20px' }}>
        <h3>v-text</h3>
        <p v-text={plainText} />
      </section>

      {/* v-bind:属性透传 */}
      <section>
        <h3>v-bind</h3>
        <div v-bind={attrs} style={{ border: '1px solid #ddd', padding: '8px' }}>
          鼠标悬停查看 title 属性
        </div>
      </section>
    </div>
  )
}

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

下一步