插槽

163 阅读4分钟

1.插槽内容

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

  • 字符串替换slot
  • 插槽还可以包含任何模板代码,包括HTML或其他组件
  • 如果组件模板中的template没有包含一个slot元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃

2.渲染作用域

当想在一个插槽中使用数据时,该插槽可以访问与模板其余部分相同的实例property(即相同的“作用域”)。

  • 插槽不能访问组件元素的作用域
  • 记住这条规则:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的

3.备用内容

有时为一个插槽设置具体的备用(也就是默认)内容是很有用的,它只会在没有提供内容的时候被渲染。

  • 例如在一个button组件中,我们希望button内绝大多数情况下都渲染文本“Submit”,为了将其作为备用内容,我们可以将它放在slot标签内
  • 在父级组件使用button组件且不提供任何插槽内容时,备用内容“Submit”将会被渲染
  • 但是如果我们提供内容,则这个提供的内容将会被渲染从而取代备用内容

4.具名插槽

有时我们需要多个插槽,对于这样的情况,slot元素有一个特殊的attribute:name,这个attribute可以用来定义额外的插槽。

  • 一个不带name的slot出口会带有隐含的名字“default”
  • 在向具名插槽提供内容时,可以在一个template元素上使用v-slot指令,并以v-slot的参数形式提供其名称
  • 注意,v-slot只能添加在template元素上(只有一种例外情况)

5.作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的。当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式。

  • 例如我们有一个组件,包含todo-items的列表
  • 我们可能会想把模板中的{{item}}替换为slot,以便在父组件上自定义
  • 但是,因为只有todo-list元素组件可以访问item,我们将从其父组件提供插槽内容
  • 要使item可用于父级提供的插槽内容,我们可以添加一个slot元素并将其作为一个attribute绑定
  • 可以根据自己的需要将很多的attribute绑定到slot上
  • 绑定在slot元素上的attribute被称为插槽prop 现在在父级作用域中,我们可以使用带值得v-slot来定义我们提供的插槽prop的名字,在例子中选择将包含所有插槽prop的对象命名为slotProps,但也可以使用任意喜欢的名字。

5.1独占默认插槽的缩写语法

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

  • 这种写法还可以更简单,就像假定未指明的内容对应默认插槽一样,不带参数的v-slot被假定对应默认插槽(省略:default)
  • 注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确
  • 只要出现多个插槽,请始终为所有的插槽使用完整的基于template元素的语法

5.2解构插槽Prop

作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个函数的函数里: function(slotProps) { //...插槽内容... }

  • 这意味着v-slot的值实际上可以是任何能够作为函数定义中的参数的JavaScript表达式
  • 也可以使用ES2015解构来传入具体的插槽prop,这样使模板更简洁,尤其是在该插槽提供了多个prop的时候
  • 解构插槽同样开启了prop重命名等其他功能,例如将item重命名为todo:v-slot="{ item: todo }"
  • 甚至可以定义备用内容,用于插槽prop是undefined的情形: v-slot="{ item = 'Placeholder' }"

6.动态插槽名

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

  • <template v-slot:[dynamicSlotName]></template>

7.具名插槽的缩写

跟v-on和v-bind一样,v-slot也有缩写,即把参数之前的所有内容(v-slot:)替换为字符#。

  • 例如v-slot:header可以被重写为#header
  • 然而和其他指令一样,该缩写只在其有参数的时候才可用,这意味着以下语法是无效的:<template #="{ item }"></template>
  • 如果希望使用缩写的话,必须始终以明确插槽名取而代之:<template #default="{ item }"></template>