Vue 的作用域插槽是个难点(共计10例)
一段时间不用,很快就忘光了。为方便自己快速回忆,特此记上一笔。
先回忆带槽组件的使用方法
例1、过时的写法
假设有一个 base-layout 组件,它带有一个 header 插槽。我们可以像下面这样使用这个组件
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>
</base-layout>
例2、啰里啰唆的写法
注意,上述slot="header"
的写法已被废弃,可以改用如下v-slot:header
的写法
<base-layout>
<template v-slot:header>
<h1>Here might be a page title</h1>
</template>
</base-layout>
例3、使用井号(推荐写法)
如果觉得这样写很麻烦,可以使用如下所示#header
的简写形式
<base-layout>
<template #header>
<h1>Here might be a page title</h1>
</template>
</base-layout>
例4、独生槽的精简写法
如果该组件有且只有一个默认插槽(独生槽),则可以去掉<template>
标签进一步简写
<base-layout>
<h1>Here might be a page title</h1>
</base-layout>
再看作用域插槽
先写一个带有作用域插槽的vue组件,取名为 user-list 组件。此组件有3个插槽,一个名叫first,一个名叫last,还有一个是默认插槽default(已省略name="default"
)
<template>
<ul class="list-unstyled">
<li v-for="user in users">
<slot :user="user">
{{user.id}}
</slot>
<slot name="first" :first="user">
{{user.firstName}}
</slot>
<slot name="last" :last="user">
{{user.lastName}}
</slot>
</li>
</ul>
</template>
<script>
export default {
props: ['users']
}
</script>
上述代码中 :first 与 :last 的命名很糟糕,它们并没有用来表示用户的姓、名,竟然指向了同一个 user 对象。不过这并不影响我们对作用域插槽的学习。
在本文的末尾,将会采用相对优雅一点的写法改写这个例子。
例5、无视插槽,直接使用组件
在blade模版中敲入代码<user-list :users="{{$users}}"></user-list>
,显示如下信息
例6、格式化 first 插槽(使用了可以随意命名的 props,不推荐)
开始玩花样了,用下面这段代码格式化firstName(改成大写,并放到【】里面)
<user-list :users="{{$users}}">
<template #first="props">
【@{{props.first.firstName.toUpperCase()}}】
</template>
</user-list>
@{{props.first.firstName.toUpperCase()}}
这行代码将会留给 Vue 解析。这里的 @ 符号提示 blade 模板:不要解析花括号里面的内容,仅仅去掉 @ 符号即可。:users="{{$users}}"
这里面的 {{$users}} 是 blade 模板语法;而 :users=”xxx” 则是 Vue 语法。
效果如下图。这里分析一下这段代码的含义:调用者是blade模板,调用的是user-list组件,调用者调用组件后对组件的first插槽做了点手脚,使得组件呈现的效果有所不同(从而达到格式化的目的)。注意这行<template #first="props">
,其中#first是不能变的,这是由插槽的名字决定的;引号里面的props是自由可变的(可以随意命名)。需要注意的是props.first.firstName.toUpperCase()
这句代码里面的first是怎么来的?这个first是组件定义插槽时决定的。组件定义插槽时有这么一行<slot name="first" :first="user">
,这里面有个:first
,就是由这个带冒号的属性决定的。
作用域插槽的对象解构
例7、格式化 last 插槽(推荐写法)
使用对象解构,会让代码简单一些
<user-list :users="{{$users}}">
<template #last="{last}">
<span class="text-primary"> @{{last.lastName}} </span>
</template>
</user-list>
效果如下图。这里面的#last="{last}"
就是对象解构,使用的时候就可以直接写成last.lastName
了。这比前例的写法要紧凑,前例多了一个自由命名的props
在定义插槽的时候,不同的插槽要取不同的名字,这点毫无疑问。但是不同插槽的属性名称是没必要区分的,属性名称完全可以取一模一样的名字。
具体来说:<slot :user="user">
、<slot name="first" :first="user">
和<slot name="last" :last="user">
,其中default插槽取了一个叫做:user的属性名称,first取了一个叫做:first的属性名,最后一个取了一个:last的属性名。这种区分完全没必要。
可以全部写成一样的:user,即<slot :user="user">
、<slot name="first" :user="user">
和<slot name="last" :user="user">
例8、插槽默认值的清空方法
再看下面这个例子,默认插槽的对象解构(格式化默认插槽,并清空first插槽及last插槽的默认数据)
<user-list :users="{{$users}}">
<template #default="{user}">
(@{{user.id}}) @{{user.name}}
</template>
<template #first><i></i></template>
<template #last><i></i></template>
</user-list>
以下为效果图
独占式作用域插槽
例9、独生带域槽的精简写法
修改user-list组件,将其中的first插槽及last插槽的定义代码全部删除,只保留一个default插槽,形成独占,称之:独占式作用域插槽(也就是组件的“独生带域槽”)。对于独生带域槽,调用的时候可以完全删除<template>
标签,但是要将#default="{user}"
挪到父标签里面(代码如下,展示效果跟上图一样)
<user-list :users="{{$users}}" #default="{user}">
(@{{user.id}}) @{{user.name}}
</user-list>
为什么要使用作用域插槽?
blade模版对user-list组件说:我给你一批用户数据,你帮我展现出来。
user-list组件说:好的,我用一个for循环依次处理。
blade模版:但是不能完全按你的那一套来,在slot这个地方要改一改。
user-list组件:具体要怎么改?
blade模版:你把材料拿过来,我演示给你看。
user-list组件:好的,给你 :user
(这一步早在插槽定义的时候就准备好了的 <slot :user="user">
)。
blade模版接过材料:看好了,我给你写个模版,你要照着模版弄(所谓的接过材料,其实就是解构出 user )
<template #default="{user}">
(@{{user.id}}) @{{user.name}}
</template>
user-list组件:好的,没问题。
通过使用作用域插槽,blade模版达到了效果定制的目的;而user-list组件也通过作用域插槽明白了调用者的意图。
为什么 blade 模板需要跟 user-list 组件索要“材料”呢?因为这里涉及到“作用域”的问题。在 blade 调用组件的时候,是访问不到组件内部数据的,但是可以访问组件对外暴露的数据。
组件内部定义插槽 | 对外暴露 | 外部使用作用域插槽 |
---|---|---|
<slot :user=”xxx”> | user | <template #default=”{user}”> |
<slot name=”first” :first=”xxx”> | first | <template #first=”{first}”> |
<slot name=”last” :last=”xxx”> | last | <template #last=”{last}”> |
<slot name=”jack” :a=”yyy” :b=”zzz”> | a & b | <template #jack=”{a, b}”> |
改写作用域插槽
首先改写 UserList.vue 文件。这次没有 default 插槽,取而代之的是 name=”id” 的插槽;再将 first 插槽和 last 插槽合并成一个 name=”name” 的插槽;另外,这一个插槽对外暴露了两项数据,此时的 :first 与 :last 已经成了名副其实的用来表示姓、名的变量了。
<template>
<ul class="list-unstyled">
<li v-for="user in users">
<slot name="id" :id="user.id">
{{user.id}}
</slot>
<slot name="name" :first="user.firstName" :last="user.lastName">
{{user.firstName}} {{user.lastName}}
</slot>
</li>
</ul>
</template>
<script>
export default {
props: ['users']
}
</script>
例10、格式化多属性槽
如此一来,blade 模板的代码也要相应调整,改成下面这样
<div class="card-body">
<user-list :users="{{$users}}">
<template #id="{id}">@{{id}}. </template>
<template #name="{first, last}">
@{{first.toUpperCase()}} <span class="text-primary">@{{last}}</span>
</template>
</user-list>
</div>
效果如下图
代码库
本文代码 git@gitee.com:zhaiduting/see.git
1、git clone git@gitee.com:zhaiduting/see.git
2、cd see; composer install; npm install; npm run dev
3、配置 .env
4、php artisan migrate –seed
5、搭建服务器
6、使用http://x.xx.x/slot
查看效果
7、版本回退,重新编译后再看效果
本作品采用《CC 协议》,转载必须注明作者和本文链接