前言
上面两篇文章介绍了组件注册和父子组件之间通信的基本方式,当然父子组件通信不光只有常用的props
属性,还有另外几种方式,不先介绍。这里主要介绍插槽。什么是插槽?在定义组件之后我想在html中另外新加入几个内容,这种情况肯定不可能再重新从组件模板中添加。插槽可以解决这个问题。
基础实例
<div id="app">
<father title="这里是标签">
插槽的使用
</father>
</div>
<template id="father">
<slot>father {{title}}</slot>
</template>
在这个实例中直接把一个<slot></slot>
作为一个元素,默认是father,但是在<father></father>
标签中我插入了一条语句,那么这条语句会直接渲染进<slot></slot>
标签中。可以注意到,在<slot></slot>
标签中有一个{{title}}
,如果打开控制台可以发现是有报错的,也就是说我们不可以使用这个值。作为一条规则要记住:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
具名插槽
有的时候,我们需要多个插槽,这种情况可以为插槽命名,使用的时候指明名字。
这是时候需要认识一个新的指令v-slot
:
使用: 可放置在函数参数位置的 JavaScript 表达式 (在支持的环境下可使用解构)。可选,即只需要在为插槽传入 prop 的时候使用。
参数: 插槽名 (可选,默认值是 default)
限用于: <template>
、组件 (对于一个单独的带 prop 的默认插槽)
<body>
<div id="app">
<child>
<!-- name=child插槽 -->
<template v-slot:child>
...
</template>
<!-- 默认插槽 -->
<template v-slot>
...
</template>
<!-- name=otherr插槽的缩写 -->
<template #otherr>
...
</template>
</child>
</div>
<template id = "child">
<div>
<h1>I am a child</h1>
<slot name="child" >child</slot>
<slot>default</slot>
<slot name="otherr">other</slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
components: {
child:{
template: "#child"
}
}
})
</script>
</body>
通过v-slot
指令,我们可以绑定命名的插槽,和v-on
,v-bind
指令一样,这条指令也有缩写#
。如果指令后面什么都没有,那么就是使用默认插槽。
作用域插槽
作用域插槽就是让插槽内容能够访问子组件中才有的数据。例如,设想一个带有如下模板的 <current-user>
组件:
<span>
<slot>{{ user.lastName }}</slot>
</span>
我们可能想换掉备用内容,用名而非姓来显示。如下:
<current-user>
{{ user.firstName }}
</current-user>
然而上述代码不会正常工作,因为只有 <current-user>
组件可以访问到 user 而我们提供的内容是在父级渲染的。
为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot>
元素的一个 attribute 绑定上去:
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
绑定在 <slot>
元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但也可以使用任意名。
接着上个具名插槽的案例,进行拓展实现:
<body>
<div id="app">
<child>
<!-- name=child插槽 -->
<template #child="value">
<p>value.cname: {{value.cname}}</p>
</template>
<!-- 默认插槽 -->
<template v-slot="slotProps">
<p> slotProps.obj:{{slotProps.obj}}</p>
</template>
<!-- name=otherr插槽的缩写 -->
<template #otherr="slotValue">
<p>slotValue.age:{{slotValue.age}}</p>
</template>
</child>
</div>
<template id="child">
<div>
<h1>I am a child</h1>
<slot name="child" :cname="name">kid</slot>
<slot :obj="obj">sdfgdf</slot>
<slot name="otherr" :age="age">other</slot>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
new Vue({
el: '#app',
components: {
child:{
data(){
return{
name:"Jock",
age:18,
obj:[{id:1,title:"aaaaa"},
{id:2,title:"bbbbb"},
{id:3,title:"ccccc"}]
}
},
template: "#child"
}
}
})
</script>
</body>
<!--
渲染效果
value.cname: Jock
slotProps.obj:[ { "id": 1, "title": "aaaaa" }, { "id": 2, "title": "bbbbb" }, { "id": 3, "title": "ccccc" } ]
slotValue.age:18
-->
总结
- 和组件中的属性template不同,这种情况下使用
<template>
,可以不用一个根元素包裹 - 可以通过v-slot:name 或者 #name进行插槽的绑定
v-slot:default="slotProps"
作用域传值,因为default 可以进行省略成v-slot="slotProps"
- 对不同插槽实行作用域的时候可以写成
v-slot:name="value"
或者简写成#name=“value”
- name已经被作为
<slot>
属性使用,不可在作用域插槽作为属性绑定