前因
这个是我这个系列的第二篇,这一篇文章我也修改过三次(2019-9-10)我是想以vue官方文档为基础,来进行理解,有人说有官方文档,还要写自己的文章干嘛,我的用意其实是我确实想把官网文档中的案例全部写一遍,来加深自己对这个知识点的理解,因为看还是不如写吧,我在吧我遇到的问题,和我的理解,写出来,以便我以后的忘记,和与我有类似经历的人不用再浪费时间
插槽
其实就是在组件的innerHTML中的内容,他会传递给这个组件中的<slot>元素,其实从功能上来看这个过程有点像prop,但用prop是传递的属性值,而插槽传递的是文本,和HTML,用法就是我们的基本格式不变我变的是其中的一些内容,这样跟好的可控性我们些组件开发的时候就可以经常用到
父组件书写
<navigation-link url="/profile">
Your Profile
</navigation-link>
子组件模板书写
<a
v-bind:href="url"
class="nav-link"
>
<slot></slot>
</a>
当组件渲染的时候,<slot></slot>
将会被替换为“Your Profile”。插槽内可以包含任何模板代码
1.包括HTML
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
2.包括其他组件
<navigation-link url="/profile">
<!-- 添加一个图标的组件 -->
<font-awesome-icon name="user"></font-awesome-icon>
Your Profile
</navigation-link>
如果 <navigation-link>的子组件模块中
没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
编译的作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
例如:
<navigation-link url="/profile">
Logged in as {{ user.name }}
</navigation-link>
在官网中这里的意思是user是子组件中的data对象中的值,他不可以再父组件中的作用域中显示会报错,其实也就是子组件中定义的值和父组件中的不同的作用域,所以引用会报错
后备(默认)内容
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
例如在一个<submit-button>
组件中他的template是:
<button type="submit">
<slot>Submit</slot>
</button>
那么他就可以在下<submit-button>
组件中没提供插槽的时候会有默认的Submit的值,只有在<submit-button>
组件中提供了值才会把默认值给替换掉
具名插槽
这里我们使用2.6版本的语法 v-slot
当我们需要多个插槽的时候,例如对于一个带有如下模板的 <base-layout>
组件他的tenplate为
<div class="container">
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况,<slot>
元素有一个特殊的特性:name
。这个特性可以用来定义额外的插槽:
<base-layout>
组件他的tenplate修改后
<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
的参数的形式提供其名称:
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>
现在 <template>
元素中的所有内容都将会被传入相应的插槽。任何没有被包裹在带有v-slot
的 <template>
中的内容都会被视为默认插槽的内容
然而,如果你希望更明确一些,仍然可以在一个 <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>
以前2.6以前用slot的写法是
<base-layout>
<template slot = 'header'>
<h1>Here might be a page title</h1>
</template>
<template slot = 'default'>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template slot = 'footer'>
<p>Here's some contact info</p>
</template>
</base-layout>
最后渲染的结果为
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
注意 v-slot
只能添加在一个 <template>
上 (只有一种例外情况,等下文),这一点和已经废弃的 slot
特性不同,建议都写。
作用域插槽
前面有说插槽(父组件中的innerHTML)的值是没办法访问子组件中的数据,但要是我们需要访问,我们要怎么办呢?
官网是这样讲的我来简单的解释一下
例如,设想一个带有如下模板的<current-user>
组件的tempalte为:
这里的前题是user是组件current-user
中的值不是父组件中的值(这里为全局)
<span>
<slot>{{ user.lastName }}</slot> //这里的{{user.lastName}}是当父组件没传slot过来的时候的默认值
</span>
组件插槽为:
<current-user>
{{ user.firstName }}
</current-user>
在这里会报错,因为我们上面说过:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。作用域中没有这个值就会报错
为了让 user
在父级的插槽内容可用,我们可以将 user
作为一个 <slot>
元素的特性绑定上去:
注意:这里一个不带 name
的 <slot>
出口会带有隐含的名字“default”
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
我的理解是这个<slot>上现在具有了一个可变化的属性他叫user,他的值是current-user组件中的user的值,这个值目前来说是一个对象
绑定在 <slot>
元素上的特性被称为插槽 prop。现在在父级作用域中,我们可以给 v-slot
带一个值来定义我们提供的插槽 prop 的名字:
<current-user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</current-user>
经过我写过后的理解就是current-user组件之后有定义一个对象user我们需要把他传对应的插槽,对应的插槽在接收通过name的值来对应相应的值,在这里
<template v-slot:default="slotProps">的意思是在这里
1.用v-slot来接收,
2.名字为default(默认值)的传过来的值,
3.并把传过来的user这个值变命名为slotProps这个对象中的一个属性,
4.所以在slotProps中找到user中的firstName了
上面有说v-slot只可以写在<template>上唯一一种情况就是:当提供的内容只有默认插槽时
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
也就是<slot>上没写name,name默认为default
具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
由于<template>中 v-slot:default=' ' 可以简写为 v-slot = ' '但是
由于下面是全部是default可以把<template>省去,把v-slot:default=' '写再父组件上
<!-- 这样会触发一个警告 -->
<current-user #="{ user }">
{{ user.firstName }}
</current-user>
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>
解构插槽prop
v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件或现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop
<current-user v-slot="{ user }">
{{ user.firstName }}
</current-user>
为什么可以这样?
上文说过
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
我们父主键接到的是slotProps对象现在我们把他解构为{ user },这样我们拿到的值就是 slotProps对象中的 user的这个属性值了
总结:
插槽看到这里我的理解就是对组件的高效利用,比如我现在写过的一个弹窗组件,这个弹窗的中间内容我们就可以在父组件中自己定义,这样这个组件的可利用价值就增加了,就不用说一种弹窗写一个组件