vue v-slot
vue v-slot相关知识
插槽内容
Vue实现了一套内容分发的API,将<slot>元素作为承载分发内容的出口。
编译作用域
当你想在一个插槽中使用数据时,例如
<navigation-link url="/profile"> Logged in as {{ user.name }} </navigation-link>
该插槽跟模板的其他地方一样可以访问相同的实例property(也就是相同的“作用域”),而不能访问<navigation-link>内部的作用域。例如url是访问不到的:
<navigation-link url="/profile">
Clicking here will send you to: {{ url }}
<!-- 这里的`url`会是undefined,因为其(指该插槽的)内容是_传递给_<navigation-link>的而不是在<navigation-link>组件*内部*定义的 -->
</navigation-link>
作为一条规则,请记住:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
后备内容
有时为一个插槽设置具体的后备(也就是默认的)内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个<submit-button>组件中:
<button type="submit"> <slot>Submit</slot> </button>
slot标签里的"Submit"就是后备内容
当我们在一个父级组件中使用<submit-button>并且不提供任何插槽内容时:
<submit-button></submit-button>
后备内容“Submit”将会被渲染:
<button type="submit">Submit</button>
但是如果我们提供内容:
<submit-button>Save</submit-button>
则这个提供的内容将会被渲染,从而取代后备内容:
<button type="submit">Save</button>
具名插槽
有时我们需要多个插槽。例如对于一个带有如下模板的<base-layout>组件:
<div class="container"> <header> <!-- 页头 --> </header> <main> <!-- 内容 --> </main> <footer> <!-- 页脚 --> </footer> </div>
对于这样的情况,<slot>元素有一个特殊的attribute: name. 这个attribute可以用来定义额外的插槽:
<div class="container"> <header> <!-- 页头 --> <slot name="header"></slot> </header> <main> <!-- 内容 --> <slot></slot> </main> <footer> <!-- 页脚 --> <slot name="footer"></slot> </footer> </div>
一个不带name的<slot>出口会带有隐含的名字default
在向具名插槽提供内容的时候,我们可以在一个<template>元素上使用v-slot指令,并以v-slot的参数的形式提供其名称:
<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>
注意:v-slot只能添加在<template>上(只有一种例外情况)
作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的<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.lastNam }} </slot> </span>
绑定在<slot>元素上的attribute被称为插槽prop,现在在父级作用域中,我们可以使用带值的v-slot来定义我们提供的插槽prop的名字:
<current-user> <template v-slot:default="slotProps"> {{ slotProps.user.firstName }} </template> </current-user>
独占默认插槽的缩写语法
在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当做插槽的模板来使用。这样我们就可以把v-slot直接用在组件上:
<current-user v-slot:default="slotProps"> {{ slotProps.user.firstName }} </current-user>
这种写法还可以更简单。就像假定未指明的内容对应默认插槽一样,不带参数的v-slot被假定对应默认插槽:
<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 -v-slot="slotProps"> <template v-slot:default="slotProps"> {{ slotProps.user.firstName }} </template> <template v-slot:other="otherSlotProps"> slotProps is NOT available here </template> </current-user>
解构插槽Prop
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数里:
function (slotProps) { // 插槽内容 }
这意味着v-slot的值实际上可以是任何能够作为函数定义中的参数的JavaScript表达式。所以在支持的环境下(单文件组件或现代浏览器),你也可以使用ES2015解构来传入具体的插槽prop, 如下:
<current-user v-slot="{ user }"> {{ user.firstName }} </current-user>
这样可以使模板更简洁,尤其是在该插槽提供了多个prop的时候。它同样开启了prop重命名等其他可能,例如将user重命名为person:
<current-user v-slot="{ user: person }"> {{ person.firstName }} </current-user>
你甚至可以定义后备内容,用于插槽prop是undefined的情形:
<current-user v-slot="{ user = { firstName: 'Guest' }}"> {{user.firstName}} </current-user>
动态插槽名
动态指令参数也可以用在v-slot上,来定义动态的插槽名:
<base-layout> <template v-slot:[dynamicSlotName]> ... </template> </base-layout>
具名插槽的缩写
跟v-on和v-bind一样,v-slot也有缩写——#
<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>
其他示例
插槽prop允许我们将插槽转换为可复用的模板,这些模板可以基于输入的prop渲染出不同的内容。这在设计封装数据逻辑同时允许父级组件自定义部分布局的可复用组件时是最有用的。
例如,我们要实现一个<todo-list>组件,它是一个列表且包含布局和过滤逻辑:
<ul> <li v-for="todo in filteredTodos" v-bind:key="todo.id"> {{ todo.text }} </li> </ul>
(未完)