- 插槽内容
- 编译作用域
<navigation-link url="/profile"> Click here will send you to: {{ url }} <!--这里的'url'会是undefined,因为其(指该插槽的)内容是传递给<navigation-link>的, 而不是在<navigation-link>组件*内部*定义的--> </navigation-link>
记住:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
- 后备内容
即在模板中定义<slot><slot>带上默认值。(如下在一个 <submit-botton> 组件中的模板)<button type="submit"> <slot>Submit</slot> </button>
- 具名插槽
- 即在模板定义中,给<slot>带上那么属性——<slot name="***"></slot>;
<div class="container"> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
- 然后在HTML中使用时,通过 <template v-slot:***></template> 调用;
<base-layout> <template v-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 v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
- 在HTML中使用时没有使用 <template v-slot:***></template> 会被视为默认插槽的的内容。或者给它们加上 <template v-slot:default></template> ;
<base-layout> <template v-slot:header> <h1>Here might be a page title</h1> </template> <template v-slot:default> <p>A paragraph for the main content.</p> <p>And another one.</p> </template> <template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
注意
v-slot
只能添加在<template>
上 (只有一种例外情况),这一点和已经废弃的slot
attribute 不同。
- 即在模板定义中,给<slot>带上那么属性——<slot name="***"></slot>;
- 作用域插槽
和前面第二点——编译作用域的问题类似,这里我们的插槽想要访问传递给父级组件的数据,是访问不到的。
这个时候就需要给插槽进行一个操作,给他一个插槽prop,和我们要访问的父级数据相绑定。(绑定在<slot>
元素上的 attribute 被称为插槽 prop。)<span> <slot v-bind:user="user"> {{ user.lastName }} </slot> </span>
这样我们就可以在HTML中使用这个组件的时候,<slot>元素中包括的内容也可以访问到父级组件的数据。
<current-user> <template v-slot:default="slotProps"> {{ slotProps.user.firstName }} </template> </current-user>
在这里我们选择将包含所有插槽 prop 的对象命名为
slotProps
,但你也可以使用任意你喜欢的名字。(可以这么理解)
-
独占 默认插槽的缩写语法
-
当被提供的内容,只有默认插槽时,组件的标签可以被当做插槽的模板来使用。这样我么就可以把 v-slot 直接用在组件上:(就是去掉<template>)
<current-user v-slot:default="slotProps"> {{ slotProps.user.firstName }} </current-user>
-
这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的
v-slot
被假定对应默认插槽:(就是去掉default)<current-user v-slot="slotProps"> {{ slotProps.user.firstName }} </current-user>
-
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<!-- 无效,会导致警告 --> <current-user v-slot="slotProps"> {{ slotProps.user.firstName }} <template v-slot:other="otherSlotProps"> slotProps is NOT available here </template> </current-user>
只要出现多个插槽,请始终为所有的插槽使用完整的基于
<template>
的语法:<current-user> <template v-slot:default="slotProps"> {{ slotProps.user.firstName }} </template> <template v-slot:other="otherSlotProps"> ... </template> </current-user>
-
-
解构插槽Prop
-
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:
function (slotProps) { // 插槽内容 }
- 这意味着
v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件或现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:<current-user v-slot="{ user }"> {{ user.firstName }} </current-user>
意思就是slotProp其实是一个JavaScript对象,我们直接把用它JavaScript对象字面量的形式表示出来。
-
可以对数据重命名
<current-user v-slot="{ user: person }"> {{ person.firstName }} </current-user>
-
可以在对象字面量中定义后备内容
<current-user v-slot="{ user = { firstName: 'Guest' } }"> {{ user.firstName }} </current-user>
-
-
- 动态插槽名
<base-layout> <template v-slot:[dynamicSlotName]> ... </template> </base-layout>
- 具名插槽的缩写
跟v-on
和v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符#
。例如v-slot:header
可以被重写为#header
:<base-layout> <template #header> <h1>Here might be a page title</h1> </template> <p>A paragraph for the main content.</p> <p>And another one.</p> <template #footer> <p>Here's some contact info</p> </template> </base-layout>
其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
<!-- 这样会触发一个警告 --> <current-user #="{ user }"> {{ user.firstName }} </current-user>
应修改为:
<current-user #default="{ user }"> {{ user.firstName }} </current-user>
- 其他示例
前面我们讲到了 <slot> 的一些作用——给在自定义组件开始标签以及结束标签之间的内容一个预定义的位置。
这个插槽就像是自定义组件A中的一个“框”,它预定了在HTML中使用这个自定义组件A时,放入这个组件开始标签与结束标签之间的内容的位置。
通过name属性定义这个 <slot> 的标签。并且这个 <slot> 是没有权利去访问到父级组件中的数据,那么就需要通过插槽Prop绑定该数据——作用域插槽。
这里我们介绍一个新玩法——通过父组件的v-for循环不断产生插槽。- 父组件
<ul> <li v-for="todo in filteredTodos" v-bind:key="todo.id" > {{ todo.text }} </li> </ul>
- 将每个todo都是父组件的插槽,然后todo作为一个插槽的prop进行绑定:
<ul> <li v-for="todo in filteredTodos" v-bind:key="todo.id" > <!-- 我们为每个 todo 准备了一个插槽, 将 `todo` 对象作为一个插槽的 prop 传入。 --> <slot name="todo" v-bind:todo="todo"> <!-- 后备内容 --> {{ todo.text }} </slot> </li> </ul>
- 然后在HTML中使用自定义组件
<todo-list v-bind:todos="todos"> <template v-slot:todo="{ todo }"> <span v-if="todo.isComplete">✓</span> {{ todo.text }} </template> </todo-list>
这只是作用域插槽用武之地的冰山一角。想了解更多现实生活中的作用域插槽的用法,我们推荐浏览诸如 Vue Virtual Scroller、Vue Promised 和 Portal Vue 等库。
- 父组件