vue slot插槽详解

插槽就是子组件中提供给父组件使用的一个占位符,用<slot></slot> 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。
插槽的目的在于使组件更具有扩展性,如何封装一个好的组件?就是将共性抽取到组件中,将不同暴露为插槽。一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容。

一、插槽基本使用

// 父组件 test.vue 内容
    <test-child>
      {
    
    {
    
    data.user}}  // 替换 <slot></slot> 里面的内容
    </test-child>

// 子组件 testChild.vue 内容
    <template>
       <div>
          <slot></slot>  // 此插槽 name 默认为 default
          // <slot> 也可以给一个后备内容 </slot>
       </div>
    </template>

注意: 如果 <test-child> 的 template 中没有包含一个 <slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃

二、具名插槽

v-slot 自 2.6.0 起有所更新。已废弃的使用 slot attribute 和 slot-scope attribute 的语法看下文第四节。

(1)基本使用

// 父组件 test.vue 内容
    <test-child>
      <template v-slot:header>  // v-slot:header 可以简写为: #header
         我是header插槽
      </template>
      
      <div>一个不带 name 的 <slot> 出口会带有隐含的名字“default”。</div>
      <p>我也属于 <slot></slot> 默认插槽内容</p>
      
      <template v-slot:footer>
         我是footer插槽
      </template>
    </test-child>

// 子组件 testChild.vue 内容
    <template>
       <header>
          <slot name="header"></slot>
       </header>
       
       <slot></slot>
          
       <footer>
          <slot name="footer"></slot>
       </footer>
    </template>

注意:v-slot 只能添加在 <template>(只有一种特例:看下文独占默认插槽

(2)独占默认插槽的缩写语法

v-slot的特例就是当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上:

<test-child v-slot>
   此子组件中只有默认插槽<slot><slot>是才能在此组件上用v-slot
</test-child>

三、作用域插槽

作用域插槽其根本目的:就是让插槽内容在父组件内能够访问子组件中才有的数据

(1)默认插槽的缩写语法

// 子组件 testChild.vue 内容
    <template>
       <div>
          <slot v-bind:user="user"> //必须绑定 插槽 prop,否则父级访问不到
             {
    
    {
    
     user.name }}
          </slot>
       </div>
    </template>


// 在父组件 test.vue 内使用
   // <test-child v-slot:default="scope"> 下面是简写
   <test-child v-slot="scope"> // scope 这个命名可以任意取
         {
    
    {
    
     scope.user.name }}
   </test-child>

绑定在 <slot> 元素上的 attribute 被称为插槽 prop现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字

补充: 在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 scope,但你也可以使用任意你喜欢的名字

(2)默认插槽 和 具名插槽 混用 注意事项

再次申明 v-slot 只能添加在 <template> 上 ,除非是默认插槽的缩写语法

错误示例:

// 这种写法是无效的,会导致警告
<test-child v-slot="slotProps">
  {
    
    {
    
     slotProps.user.name }}
  <template v-slot:other="otherSlotProps">
    slotProps is NOT available here
  </template>
</test-child>

正确写法: 只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template> 的语法:

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

  <template v-slot:other="otherSlotProps">
    ...
  </template>
</test-child>

注意:默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:

(3) 解构插槽 Prop

作用域插槽的内部工作原理:是将你的插槽内容包裹在一个拥有单个参数的函数里:

function (slotProps) {
    
    
  // 插槽内容
}

这意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:

<test-child v-slot="{ user }">
  {
    
    {
    
     user.name }}
</test-child>

这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 user 重命名为 person

<test-child v-slot="{ user: person }">
  {
    
    {
    
     person.name }}
</test-child>

你甚至可以定义后备内容,用于插槽 prop 是 undefined 的情形:

// 子组件 test-child 的内容 
<template>
    <slot></slot>
</template>

// 父组件写法 (子组件没有绑定任何attribute 的时候可自定义后备数据)
<test-child v-slot="{ user = { name: 'Guest' } }">
  {
    
    {
    
     user.name }}
</test-child>

四、动态插槽名

动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

五、废弃的具名插槽

v-slot 指令自 Vue 2.6.0 起被引入,提供更好的支持 slotslot-scope attribute 的 API替代方案。在接下来所有的 2.x 版本中 slotslot-scope attribute仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。

(1)带有 slot attribute 的具名插槽

<template> 上使用特殊的 slot attribute,可以将内容从父级传给具名插槽:

<base-layout>
  <template slot="header">
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template slot="footer">
    <p>Here's some contact info</p>
  </template>
</base-layout>

或者直接把 slot attribute 用在一个普通元素上:

<base-layout>
  <h1 slot="header">Here might be a page title</h1>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <p slot="footer">Here's some contact info</p>
</base-layout>

注意: v-slotslot的用法有一个很大的区别: slot可以用在一个普通元素上,而 v-slot 只能在template上(当然还有一个特例,看上文)

(2)带有 slot-scope attribute 的作用域插槽

<template> 上使用特殊的 slot-scope attribute,可以接收传递给插槽的 prop

<slot-example>
  <template slot="default" slot-scope="slotProps">
    {
    
    {
    
     slotProps.msg }}
  </template>
</slot-example>

这里的 slot-scope 声明了被接收的 prop 对象会作为 slotProps 变量存在于 <template> 作用域中。你可以像命名 JavaScript 函数参数一样随意命名 slotProps

这里的 slot="default" 可以被忽略为隐性写法:

<slot-example>
  <template slot-scope="slotProps">
    {
    
    {
    
     slotProps.msg }}
  </template>
</slot-example>

slot-scope attribute 也可以直接用于非 <template> 元素 (包括组件):

<slot-example>
  <span slot-scope="slotProps">
    {
    
    {
    
     slotProps.msg }}
  </span>
</slot-example>

slot-scope 的值可以接收任何有效的可以出现在函数定义的参数位置上的 JavaScript 表达式。这意味着在支持的环境下 (单文件组件或现代浏览器),你也可以在表达式中使用 ES2015 解构,如下:

<slot-example>
  <span slot-scope="{ msg }">
    {
    
    {
    
     msg }}
  </span>
</slot-example>

使用这里描述过的 <todo-list> 作为示例,与它等价的使用 slot-scope 的代码是:

<todo-list v-bind:todos="todos">
  <template slot="todo" slot-scope="{ todo }">
    <span v-if="todo.isComplete"></span>
    {
    
    {
    
     todo.text }}
  </template>
</todo-list>

猜你喜欢

转载自blog.csdn.net/qq_44094296/article/details/123253597