Vue 内容分发

Vue 实现了一套内容分发的 API,其设计灵感源自 Web Components 规范草案,将 <slot>元素作为承载分发内容的出口。

插槽,实质上是,子组件中的提供给父组件使用的一个占位符,用 <slot></slot> 元素表示( 位于子组件中 )。

  • 插槽内容:父组件可以在这个占位符中填充任何内容,如 HTML、组件等
    • 具名插槽( 父:v-slot 指令,子:<slot name=""></slot> )
  • 插槽的使用:填充的内容会替换子组件的<slot></slot>标签
    • ( 子组件 )默认插槽( 又称为:后备内容 )
    • 作用域插槽:让父组件的插槽内容,能够访问子组件中已有的数据

使用场景:一般用于子组件的“模板结构”类似,而内容不同的情形( 通过父组件进行内容分发 )


基本用法

// 声明一个子组件
<div id="app">
    <child-component></child-component>
</div>
<script>
    Vue.component('child-component',{
        template:‘<div>Hello,World!</div>’
    })
</script>

我们可能需要向这个子组件中传递内容,像下面这样,结果会是怎样呢?

<child-component>
  你好    // 可以是任何内容
</child-component>

输出内容还是在组件中的内容,在 <child-component>内写的内容没起作用。怎么办呢?


还好,Vue 自定义的 <slot>元素( 插槽 )让这变得非常简单:

插槽,实际上就是调用组件时,放在( 父 )组件标签中传递内容的相应的子组件内部,

需要有 <slot> 标签来接收传递过来的内容,否则传递过来的任何内容都会被抛弃。

// 父组件
<template>
  <div id="app">
    <div>父组件的内容</div>
    <!-- 引用子组件并传递内容 -->
    <Child>HelloWorld</Child>
  </div>
</template>

// 子组件
<template>
  <div id="content">
    <div>子组件内容</div>
    <!-- 通过插槽接收内容 -->
    <slot></slot>
  </div>
</template>

插槽的使用


  • 默认插槽,又称为后备内容

后备内容放在 slot 标签中,有时为一个插槽设置具体的后备内容是很有用的,它只会在没有提供内容的时候被渲染。

// 父组件
<template>
  <div id="app">
    <div>父组件的内容</div>
    <!-- 引用子组件:如果父组件中没有提供任何插槽内容,则"后备内容"将会被渲染 -->
    <Child></Child>
    <!-- 如果父组件中提供了插槽内容,则插槽内容将会替代“后备内容”被渲染 -->
    <!-- <Child>点击</Child> -->
  </div>
</template>

// 子组件
<template>
  <div id="content">
    <!-- 默认插槽:后备内容放在 slot 标签中 -->
    <button><slot>Submit</slot></button>
  </div>
</template>

  • 具名插槽

自 2.6.0 起有所更新,已废弃使用 slot特性的语法

      <template slot="header">
        <div>头部的内容</div>
      </template>

有时候,可能需要使用多个插槽。在向具名插槽提供内容的时,可以在一个 <template>元素上使用 v-slot指令,并以 v-slot的参数的形式提供其名称

// 父组件 baseLayout.vue
<template>
  <div id="app">
    <div>父组件的内容</div>
    <!-- 引用子组件并传递内容 -->
    <Child>
      <template v-slot:header>
        <div>头部的内容</div>
      </template>
      <div>主体的内容</div>   // 不具名,则其插槽的 name 值默认为“default”
      // v-slot: 可以简写为 #
      <template #footer>
        <div>底部的内容</div>
      </template>
    </Child>
  </div>
</template>

<slot>元素有一个特殊的特性:name,可以用来定义额外的插槽

// 子组件
<template>
  <div id="content">
    <!-- 具名插槽 -->
    <div class="header">
      <slot name="header"></slot>
    </div>
    // 一个不带name的<slot>默认会带有一个隐含的名字“default”,即:<slot name="default"></slot>
    <slot></slot>
    <div class="footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

  • 作用域插槽

自 2.6.0 起有所更新。已废弃使用 slot-scope特性的语法


关于插槽的作用域有一条基本规则:

父级模板里的所有内容都是在父级作用域中编译的,子模板里的所有内容都是在子作用域中编译的,二者相互隔离。

有时候,让插槽内容能够访问子组件中已有的数据是很有用的

插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。

<!-- 子组件 Child.vue -->
<template>
  <div id="content">
    <!-- 绑定在 <slot>元素上的特性被称为插槽 prop -->
    <slot :person="person"></slot>
  </div>
</template>
 
<script>
export default{
  data(){
    return{
      person:{
        name:'小明',
        age:20
      }
    }
  }
}
</script>
// 父组件
<template>
  <div id="app">
    <div>父组件的内容</div>
    <!-- 引用子组件并传递内容 -->
    <!-- 在父级作用域中,我们可以给 v-slot带一个值来定义我们提供的插槽 prop 的名字 -->
    <!-- slotProps可以自定义 -->
    <Child v-slot:default='slotProps'>
      {{slotProps.person.name}}
      <!-- 小明 -->
    </Child>
  </div>
</template>