前因
这个是我这个系列的第二篇,我是想以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”。插槽内可以包含任何模板代码
包括HTML
<navigation-link url="/profile">
<!-- 添加一个 Font Awesome 图标 -->
<span class="fa fa-user"></span>
Your Profile
</navigation-link>
包括其他组件
<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是组件(navigation-link)中的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
。这个特性可以用来定义额外的插槽:
修改后
<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>
最后渲染的结果为
<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>
上 (只有一种例外情况,等下文)
作用域插槽
前面有说插槽(组件中的innerHTML)的值是没办法访问子组件中的数据,但要是我们需要访问,我们要怎么办呢?
官网是这样讲的我来简单的解释一下
例如,设想一个带有如下模板的<current-user>
组件的tempalte为:
这里的前题是user是组件current-user
中的值不是父组件中的值(这里为全局)
<span>
<slot>{{ user.lastName }}</slot>
</span>
组件插槽为:
<current-user>
{{ user.firstName }}
</current-user>
在这里会报错,因为我们上面说过:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。作用域中没有这个值就会报错
为了让 user
在父级的插槽内容可用,我们可以将 user
作为一个 <slot>
元素的特性绑定上去:
<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">的意思是在这里用v-slot来接收,名字为default(默认值)的传过来的值,并把这个值变命名为slotProps这个对象中的一个属性,所以在slotProps.user.firstName就可以找到user中的firstName了
上面有说v-slot只可以写在<template>上唯一一种情况就是:当被提供的内容只有默认插槽时
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
也就是<slot>上没写name,name默认为default;(但我个人建议还是用<tenplate>);
具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
<!-- 这样会触发一个警告 -->
<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>
其实根据上面的插槽prop和解构(无声明赋值做一个部分)的理解我而为是把我们写的对象{user} 他等于slot传过来的对象了,那么我们写的对象中的user这个属性值就会等于slot传过来的对象中的user的属性值
其实这里的解构插槽我肯了一段时间也不是很懂?如果大家看了之后有懂的可以教下我
我在这里吧我用到的案例(解构的案例发一下),大家看的会理解跟深
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<my-component-name>
<template lang="pug" v-slot:zqx="{user , zqx}"> //这里的zqx只是名字
{{user.zqx}}{{zqx.name}}
</template>
</my-component-name>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
Vue.component('my-component-name',{
template:`
<div
class="nav-link"
>
//传了2个porp这个也是解构的好处
<slot v-bind:user="user" :zqx = 'zqx' name="zqx"></slot>
</div>
`,
data() {
return {
url:'#',
user:{
item: 3,
zqx: '帅'
},
zqx:{
eag : 18,
name : 'zqx',
item: 180
}
}
},
})
new Vue({
el:'#app',
//这样的写法好点,和组件一样的写法,确保每一次返回的值(引用类型)都是不全等的
//因为每一次都会重新执行一次data(),所以返回值都是新的
data() {
return {
}
},
})
</script>
</body>
</html>
总结:
插槽看到这里我的理解就是对组件的高效利用,因为有些组件逻辑结构相同的时候,就是有一些内容需要换的时候,就可以使用插槽