[vue3 vs vue2] slot 用法详解

5,054 阅读2分钟

通过对比 vue3 和 vue2 的语法的异同加以练习来学习vue3。来看下 slot 的在 2.x和 3.x的用法。

2.x syntax

在 2.x 中主要掌握这两个的用法。
• $slots
• $scopedSlots

vm.$slots 模版用法

Type: { [name: string]: ?Array<VNode> }

未指明slot name 定义在$slots.default ,指定 propsName 定义在 $slots.propsName

// fahter
<template>
    <Book>
      <span>默认值</span>
      <span slot="header">header</span>
      <span slot="content">content1</span>
      <span slot="content">content2</span>
    </Book>
</template>
// child
<template>
  <div>
    <slot>default 1</slot>
    <slot name="header">default 2</slot>
    <slot name="content">default 3</slot>
  </div>
</template>
  • slot 默认
  • slot name 具名slot,这样我们可以指定多个 slot,并任意排列位置,实现布局需求。

vm.$slots 渲染函数中用法

首先来看下渲染函数的语法

createElement('div',{//...data object},[
    'some text',
    createElement('h1'),
    createElement(MyComponent,{props:{someProp:'foobar'}})
])

• 参数1: Book 组件
• 参数2: 是对第一个参数 {html tag | component} 进行参数设置,具体参考 createElement 参数
• 参数3: children VNodes {String | Array}可以字符串,有多个使用数组的形式

  render(h) {
    return h(Book,{},[
      h('span',{},'默认值'),
      h('span',{slot:'header'},'header'),
      h('span',{slot:'content'},'content1'),
      h('span',{slot:'content'},'content2')
    ]) 
  }

实现自定义默认值

export default {
  render(h) {
    return h('div',{},[
      this.$slots.default || 'default1',
      this.$slots.header  || 'default2',
      this.$slots.content  || 'default3'
    ])
  }
}

vm.$scopedSlots 模版用法

Type: { [name: string]: props => Array<VNode> | undefined }

与 $slots 的区别是,$scopedSlots 可以从组件内部向父级作用域传递数据。

<template>
  <div>
    <Child>
      <template v-slot:default="slotProps">
        {{ slotProps.user.name }}
      </template>
    </Child>
  </div>
</template>
<template>
  <div>
    <slot :user="user"></slot>
  </div>
</template>

vm.$scopedSlots 渲染函数中用法

  • $scopedSlots.default 是一个函数可以想父组件传递数据
  • 父组件中通过 scopedSlots 来接收子组件传递的数据,使用方法如下:
  // fahter
  render(h) {
    return h(Book,{
            scopedSlots: {
              default:function(props){
                return [ 
                        props.user.name,
                        <div>text</div>,
                        'sdfsdf'
                      ]
              }
            } 
          })
  }
   // Book.vue
    render(h) {
      return h('div',{},[
        this.$scopedSlots.default({
          user:this.user 
        })
      ])
    }

3.x syntax

• this.$slots 现在将 slots 作为函数公开 (2.x是一个对象)
• 非兼容:移除 this.$scopedSlots
• 指定函数来定义传递给子组件的 slot 语法:h(Component,{},{header:()=>Component),使用方式如下:

// father
<script lang='ts'>
  import { h } from 'vue';
  import Child from './Child.vue'
  export default {
    components:{
      Child
    },
    render() {
      return h(Child,{},
        { 
          header:(props:{year:number})=>{
            return h('div',`this year is ${props.year}`)
          },
          title:()=> h('h1', {
            innerHTML:'标题',
            onClick(){
              console.log('点击了标题')
            },
            style:{
              color:'#f66'
            }
          })
        }
      )
    }
  }
</script>
// child
<script lang='ts'>
  import { h } from 'vue';
  export default {
    render() {
      return h('div',[this.$slots.title(),this.$slots.header({year:2021})])
    }
  }
</script>

• h 现在全局导入,而不是作为参数传递给渲染函数,可以查看官网详细信息。
• 现在 slot 是指定一个函数(2.x 是直接定义一个VNode,h('span',{slot:'header'},'header'))
• 这里之所以指定一个函数来代替之前 scopedSlot 的功能,这样组件内部可以将参数传递出来

  • h(Child,{},{header:Function}) 使用 scopedSlots 时候第三个参数必须为对象
    • createElement 参数第三个参数 {String | Array} ,猜测这里必须为对象是想通过 propName 才找到对应的函数来渲染我们指定的 header、title VNode 。如果有多个子元素无法确定是哪个。

h 渲染函数

这里还需要注意的是,在 3.x 中渲染函数的语法有些改动如下:

2.x 语法

在 2.x 中,domProps 包含 VNode props 中的嵌套列表:

// 2.x
{
  class: ['button', 'is-outlined'],
  style: { color: '#34495E' },
  attrs: { id: 'submit' },
  domProps: { innerHTML: '' },
  on: { click: submitForm },
  key: 'submit-button'
}

3.x 语法

在 3.x 中,整个 VNode props 结构是扁平的,使用上面的例子,下面是它现在的样子

// 3.x 语法
{
  class: ['button', 'is-outlined'],
  style: { color: '#34495E' },
  id: 'submit',
  innerHTML: '',
  onClick: submitForm,
  key: 'submit-button'
}

参考

中文文档