Dynamic

Dynamic 是动态视图构建器,用于根据条件动态渲染不同的组件或 HTML 元素。当渲染目标发生变化时,Dynamic 会自动切换渲染内容。

基本用法

动态组件

tsx
import { Dynamic, shallowRef } from 'vitarx'

function ComponentA() {
  return <div>组件 A</div>
}

function ComponentB() {
  return <div>组件 B</div>
}

function App() {
  const current = shallowRef(ComponentA)

  return (
    <div>
      <button
        onClick={() => {
          current.value = ComponentA
        }}
      >
        显示 A
      </button>
      <button
        onClick={() => {
          current.value = ComponentB
        }}
      >
        显示 B
      </button>
      {/* 动态渲染 current 指向的组件 */}
      <Dynamic is={current} />
    </div>
  )
}

动态 HTML 标签

Dynamic 也可以动态渲染不同的 HTML 标签:

tsx
import { Dynamic, shallowRef } from 'vitarx'

function App() {
  const tag = shallowRef<'div' | 'span' | 'p'>('div')

  return (
    <div>
      <button
        onClick={() => {
          tag.value = 'div'
        }}
      >
        div
      </button>
      <button
        onClick={() => {
          tag.value = 'span'
        }}
      >
        span
      </button>
      <button
        onClick={() => {
          tag.value = 'p'
        }}
      >
        p
      </button>
      {/* 动态渲染不同的 HTML 标签 */}
      <Dynamic is={tag}>这段内容的标签会变化</Dynamic>
    </div>
  )
}

传递属性

Dynamic 上除 iskeymemochildren 以外的属性,都会原样传递给渲染的组件或元素:

tsx
<Dynamic is={current} title="hello" className="active" />

属性

属性类型必填说明
isComponent | HostElementTag | Ref<any> | undefined | null | false动态渲染目标
keyunknown唯一标识,配合 memo 缓存同一组件的不同实例
memoboolean | number缓存策略,默认 false 不缓存
childrenRenderChildren子节点,传递给动态组件或元素
...其他属性any其他属性会原样传递给渲染的组件或元素

memo — 缓存策略

Dynamic 默认不缓存切换掉的组件视图。如果你希望切换后再切回来时复用之前的视图,可以开启 memo

memo 值行为
false(默认)不缓存,切换时销毁旧视图
true缓存所有组件视图,不限制数量
number > 0缓存指定数量的组件视图,采用 LRU 策略淘汰
number <= 0等同于 true,不限制数量

注意memo 仅对组件函数有效,HTML 标签不会被缓存。如果你需要更完整的缓存功能(如 include/exclude),请使用 Freeze 组件。

tsx
import { Dynamic, shallowRef } from 'vitarx'

function PageA() {
  return <div>页面 A</div>
}
function PageB() {
  return <div>页面 B</div>
}
function PageC() {
  return <div>页面 C</div>
}

function App() {
  const current = shallowRef(PageA)

  return (
    // 最多缓存 3 个组件视图
    <Dynamic is={current} memo={3} />
  )
}

Dynamic 与 Freeze 的区别

特性DynamicFreeze
缓存策略简单的 memo 缓存完整的缓存管理(include/exclude/max)
组件冻结切换时销毁或缓存切换时冻结响应式,保留完整状态
适用场景简单的动态渲染需要保留组件状态的 Tab 页签等
HTML 标签支持不支持

简单来说:如果只是动态切换组件,用 Dynamic;如果需要保留组件状态,用 Freeze

完整示例

下面是一个使用 Dynamic 实现动态表单的完整示例:

tsx
import { Dynamic, shallowRef, ref } from 'vitarx'

/** 文本输入表单 */
function TextForm() {
  const value = ref('')
  return (
    <div>
      <h3>文本输入</h3>
      <input
        value={value}
        onInput={(e) => {
          value.value = (e.target as HTMLInputElement).value
        }}
        placeholder="请输入文本"
      />
      <p>当前值:{value}</p>
    </div>
  )
}

/** 数字输入表单 */
function NumberForm() {
  const value = ref(0)
  return (
    <div>
      <h3>数字输入</h3>
      <input
        type="number"
        value={value}
        onInput={(e) => {
          value.value = Number((e.target as HTMLInputElement).value)
        }}
      />
      <p>当前值:{value}</p>
    </div>
  )
}

/** 选择表单 */
function SelectForm() {
  const value = ref('apple')
  return (
    <div>
      <h3>选择</h3>
      <select
        value={value}
        onChange={(e) => {
          value.value = (e.target as HTMLSelectElement).value
        }}
      >
        <option value="apple">苹果</option>
        <option value="banana">香蕉</option>
        <option value="orange">橘子</option>
      </select>
      <p>当前选择:{value}</p>
    </div>
  )
}

function App() {
  const formTypes = [
    { label: '文本', component: TextForm },
    { label: '数字', component: NumberForm },
    { label: '选择', component: SelectForm }
  ]
  const current = shallowRef(TextForm)

  return (
    <div>
      <h2>动态表单</h2>
      <div style="margin-bottom: 16px;">
        {formTypes.map((item) => (
          <button
            key={item.label}
            onClick={() => {
              current.value = item.component
            }}
            style={{
              marginRight: '8px',
              fontWeight: current.value === item.component ? 'bold' : 'normal'
            }}
          >
            {item.label}
          </button>
        ))}
      </div>
      {/* 动态渲染当前选中的表单组件 */}
      <Dynamic is={current} />
    </div>
  )
}

下一步