Skip to content

Spread 合并

完整 VNodeData spread

jsx
const data = {
  class: ['card'],
  attrs: { title: 'title' },
  on: { click: handleClick }
}

return <article {...data} />

多个数据段通过虚拟 runtime 按 Vue 2 规则合并:

  • attrspropsdomProps 浅合并
  • onnativeOn 的同名 handler 合并为数组
  • hook 的同名 hook 合并为顺序调用函数
  • classstyle 保留组合顺序
  • directives 按出现顺序合并并展平为单层数组
  • 其他根属性以后出现者为准

推荐:分组也放入显式 VNodeData

jsx
const buttonData = {
  attrs: {
    'aria-label': 'save'
  },
  on: {
    click: save
  }
}

return <button {...buttonData}>保存</button>

插件也兼容官方 Babel preset 的 attrs={attrs}on={listeners} 分组属性,但 Demo 优先使用完整 data object,避免把业务属性和 VNodeData 分组写法混在一起。

Children spread

vue
<script lang="jsx">
export default {
  name: 'SpreadDemo',
  directives: {
    mark: {
      bind(element, binding) {
        element.dataset.mark = String(binding.value)
      }
    }
  },
  data() {
    return { count: 0 }
  },
  render() {
    const vnodeData = {
      class: ['spread-card'],
      attrs: { title: '完整 VNodeData spread', 'data-spread': 'root' },
      on: { dblclick: () => { this.count += 2 } }
    }
    const buttonData = {
      attrs: {
        'data-group': 'attrs spread',
        'aria-label': 'spread demo'
      },
      on: {
        click: () => { this.count += 1 }
      }
    }
    const firstDirectives = [
      { name: 'mark', value: 'first' }
    ]
    const secondDirectives = [
      { name: 'mark', value: 'second', modifiers: { merged: true } }
    ]
    const nodes = [
      <span class="badge" key="a">spread child A</span>,
      <span class="badge" key="b">spread child B</span>
    ]

    return (
      <article {...vnodeData} class="demo-card">
        <span class="case-label">spread merge</span>
        <h3>完整 VNodeData、directives 与 children spread</h3>
        <p>多个 directives 数组会按出现顺序合并为同一个扁平数组。</p>
        <button {...buttonData} class="button">click + 1 / dblclick + 2</button>
        <div
          {...{ directives: firstDirectives }}
          {...{ directives: secondDirectives }}
          class="result-line"
        >
          directives spread 合并结果:data-mark=second
        </div>
        <div class="badge-row">{...nodes}</div>
        <p>count:{this.count}</p>
      </article>
    )
  }
}
</script>

directives spread

jsx
const directives = [
  { name: 'my-dir', value: 123, modifiers: { abc: true } }
]

return <div {...{ directives }} />

也可以和其他指令数据及 JSX 指令混用:

jsx
return (
  <div
    {...{ directives: first }}
    {...{ directives: second }}
    vShow={visible}
  />
)

最终 VNodeData.directives 始终是扁平数组,不会生成 [[directiveA], [directiveB]]

基于 Oxc Parser 的 Vue 2.7 JSX/TSX 转换器