vue插槽
什么是插槽
要了解插槽,那什么是插槽呢?我认为组件化编程用的多,用处嘛,就是先“占坑”,后面内容是什么就替换成什么。
插槽内容
先定义两个组件,parent.vue和test.vue,在parent.vue组件中引用test.vue组件
//parent.vue
<test >Hello world </test>
//test.vue
<template>
<a href="#">
<slot></slot>
</a>
</template>
运行parent.vue
相当于parent.vue被渲染成
<a href="#">
Hello world
</a>
简单的一个小例子,插槽当然可以替代的内容很多,不只是字符串
//home.vue
<test>
<!-- 添加一个图标的组件,用的Elementui组件 -->
<i class="el-icon-share" ></i>
Hello world
</test>
结果都是可以的
如果 test组件 的 template 中没有包含一个 slot元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃,就是你test组件中间即使有内容也不会被渲染成上面之前的结果的。
编译作用域
你不可能一直用的数据都是死的,当你想在一个插槽中使用数据时,例如:
//parent.vue
<test>
//插槽可以获取到parent组件里的内容
Hello {{msg}}
</test>
data(){
return{
msg:'world'
}
}
//parent.vue
//这里是获取不到name的,因为这个值是传给<test>的
<test name='Mike'>
Hello {{name}}
</test>
规则:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
后备内容(默认内容)插槽
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。例如在一个 组件中:
<button type="submit">
<slot></slot>
</button>
我们可能希望这个 内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 标签内:
<button type="submit">
<slot>Submit</slot>
</button>
现在当我在一个父级组件中使用 并且不提供任何插槽内容时:
<submit-button></submit-button>
后备内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
但是如果我们提供内容:
<submit-button>
Save
</submit-button>
则这个提供的内容将会被渲染从而取代后备内容:
<button type="submit">
Save
</button>
具名插槽
有时候我们一个组件里需要多个插槽
那么怎么办呢? 对于这样的情况,元素有一个特殊的特性:name ,这个特性可以用来定义额外的插槽
<div>
<header>
<!-- 我们希望把页头放这里 -->
</header>
<main>
<!-- 我们希望把主要内容放这里 -->
</main>
<footer>
<!-- 我们希望把页脚放这里 -->
</footer>
</div>
对于这样的情况, 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
一个不带 name 的 出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 元素上使用 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 的 中的内容都会被视为默认插槽的内容。
然而,如果你希望更明确一些,仍然可以在一个 中包裹默认插槽的内容:
<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 只能添加在 上 (只有一种例外情况,后面的独占默认插槽的缩写语法会讲到),这一点和已经废弃的 slot attribute 不同。
作用域插槽
上面已经说了,插槽跟模板其他地方一样都可以访问相同的实例属性(也就是相同的"作用域"),而不能访问的作用域
那如果想访问作用域该怎么办呢?
我们把需要传递的内容绑到 上,然后在父组件中用v-slot设置一个值来定义我们提供插槽的名字:
//test.vue
<template>
<a href="#">
<!-- 设置默认值:{{user.lastName}}获取 Jackson-->
<!-- 如果home.vue中给这个插槽值的话,则不显示 Jackson-->
<!-- 设置一个 user然后把user绑到设置的 user上 -->
<slot v-bind:user="user">{{ user.lastName }}</slot>
</a>
</template>
<script>
export default {
name: "test",
data() {
return {
user: {
firstName: "Michael",
lastName: "Jackson",
},
};
},
};
</script>
//然后parent.vue接受传过来的值
//home.vue
<div>
<test v-slot:default="slotProps">
{{slotProps.user.firstName}}
</test>
</div>
结果也是 Michael
绑定在 <slot>元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字。在这个例子中,我们选择将包含所有插槽 prop 的对象命名slotProps,但你也可以使用任意你喜欢的名字。
独占默认插槽的缩写语法
在上述情况下,当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot 直接用在组件上:
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user>
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
这两种写法都是一样的,第二种写法更简单。就像假定未指明的内容对应默认插槽一样,不带参数的 v-slot 被假定对应默认插槽。
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确:
<!-- 无效,会直接报错 -->
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
<template v-slot:other="otherSlotProps">
slotProps is NOT available here
</template>
</current-user>
只要出现多个插槽,请始终为所有的插槽使用完整的基于 的语法:
<test>
<template v-slot:default="user">
{{ user.firstName }}
</template>
<template v-slot:other> slotProps is NOT available here </template>
</test>
解构插槽 Prop
因为 作用域插槽 的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里,这意味着 v-slot 的值实际上可以是任何能够作为函数定义中的参数的 JS 表达式
所以本来是这样写的:
<test v-slot="slotProps">
{{slotProps.user.firstName}}
</test>
还可以这样写:
<test v-slot={user}>
{{user.firstName}}
</test>
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,
例如可以将 user 重命名为 person:
<test v-slot={user:person}>
{{person.firstName}}
</test>
你甚至可以定义后备内容,用于插槽 prop 是 undefined 的情形(这里要举个例子):
//parent.vue
<!-- 定义后备内容,用于插槽prop是undefined的情形 -->
<test v-slot="{ user = { firstName:'qiu' } }">
{{ user.firstName }}
</test>
//test.vue
<template>
<span>
<slot :user = "user"></slot>
</span>
</template>
<script>
export default {
name: 'test',
data(){
return {
user: undefined
}
}
}
</script>
动态插槽名
具名插槽的缩写
跟 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>
需要注意的是,#后面必须要跟参数,#default不能缩写为#
其它示例
废弃了的语法
带有 slot attribute 的具名插槽,带有 slot-scope attribute 的作用域插槽
这里把他们放在一起看,下面两个是等价的
<test slot="aaa" slot-scope="slotProps" >
{{slotProps.user.firstName}}
</test>
<test v-slot:aaa="slotProps">
{{slotProps.user.firstName}}
</test>
感觉v-slot有点语法糖的意思
搞定收工