Vue 插槽全攻略:重塑组件灵活性

南木元元 2024-10-07 17:33:29 阅读 87

前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元


目录

什么是slot插槽

默认插槽

编译作用域

后备内容

具名插槽

作用域插槽

应用场景

动态插槽名

废弃的插槽语法

默认插槽

具名插槽

作用域插槽

总结

结语


什么是slot插槽

Vue官方文档:Vue实现了一套内容分发的 API,将<slot>元素作为承载分发内容的出口。

通俗来讲,就是“占坑”,使用<slot></slot>在子组件模板中占好位置,父组件就可以向其中传递一些内容。

Vue 的插槽可以分为三类:

默认插槽具名插槽 作用域插槽

默认插槽

默认插槽又名匿名插槽,是最简单的插槽形式,当slot没有指定name属性值的时候为一个匿名插槽(一个组件内只有一个匿名插槽)。

示例:

<code>// 子组件Child

<template>

<div class="child">code>

<!-- 默认插槽:这个内容会被父组件传递的内容替换 -->

<slot></slot>

</div>

</template>

// 父组件Parent(引用子组件Child)

<template>

<Child>南木元元</Child>

</template>

渲染结果:

<template>

<div class="child">code>

南木元元

</div>

</template>

组件中使用默认插槽slot,实现了父组件向子组件的指定位置插入一段内容,即内容的分发。

编译作用域

我们可以通过slot向子组件传递父组件内任意的data值,比如:

<code>// 父组件Parent(引用子组件Child)

<template>

<Child>{ { msg }}</Child>

</template>

<script>

import Child from "./child.vue";

export default {

components: {

Child,

},

data() {

return {

msg: "父组件",

};

},

};

</script>

但我们能否直接使用子组件内的数据呢?答案是不行的。

// 子组件Child

<template>

<div class="child">code>

<!-- 默认插槽:这个内容会被父组件传递的内容替换 -->

<slot></slot>

</div>

</template>

<script>

export default {

data() {

return {

child: "子组件",

};

},

};

</script>

// 父组件Parent(引用子组件Child)

<template>

<!-- 不能访问子组件的作用域 -->

<Child>{ { child }}</Child>

</template>

因为有一条规则:

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

后备内容

后备内容,就是slot的默认值,有时我们没有在父组件中提供内容,那么slot就会显示默认值,如:

// 子组件Child

<template>

<div class="child">code>

<!-- 父组件没有提供内容时显示后备内容 -->

<slot>这是插槽的后备内容</slot>

</div>

</template>

// 父组件Parent(引用子组件Child)

<template>

<!-- 父组件没有向插槽提供内容 -->

<Child></Child>

</template>

渲染结果: 

具名插槽

具名插槽是指带有name属性的slot,一个组件可以出现多个具名插槽。

有时需要多个插槽,我们就可以使用具名插槽,根据名称将不同内容精确分发到不同位置。

示例:

子组件

<code>// 子组件Child

<template>

<div class="child">code>

<!-- 具名插槽:带name的slot -->

<header>

<slot name="header"></slot>code>

</header>

<main>

<slot></slot>

</main>

<footer>

<slot name="footer"></slot>code>

</footer>

</div>

</template>

组件中,需要在一个 <template> 元素上使用 v-slot:name#name(v-slot:name的缩写,2.6.0 新增)的形式向具名插槽提供内容。

// 父组件Parent(引用子组件Child)

<template>

<Child>

<!-- 具名插槽header -->

<template v-slot:header>

<h1>头部标题区</h1>

</template>

<!-- 这是默认插槽 -->

<p>主要内容区</p>

<!-- 具名插槽footer,使用缩写形式 -->

<template #footer>

<p>底部区域</p>

</template>

</Child>

</template>

渲染结果:

注意:上面示例中,<template>元素中的所有内容都将会被传入相应的插槽,而没有被包裹在带有v-slot的<template>中的内容都会被视为默认插槽的内容。

<code><p>主要内容区</p>

// 上面等价于下面这种写法

<template v-slot:default>

<p>主要内容区</p>

</template>

作用域插槽

前面我们说过,父组件中不能直接使用子组件内的数据。如果父组件要使用子组件中的数据,我们可以使用作用域插槽。该插槽可以将子组件数据传给父组件,让父组件根据子组件的传递过来的数据决定如何渲染内容。

// 子组件Child

<template>

<div class="child">code>

<header>

<!-- 向具名插槽中传入子组件的数据 -->

<slot name="header" :message1="child1"></slot>code>

</header>

<main>

<!-- 传入子组件的数据,这里没有命名slot -->

<slot :message2="child2"></slot>code>

</main>

</div>

</template>

<script>

export default {

data() {

return {

child1: "消息1",

child2: "消息2",

};

},

};

</script>

// 父组件Parent(引用子组件Child)

<template>

<Child>

<!-- 通过v-slot的语法,将子组件的message1值赋值给headerProps -->

<template v-slot:header="headerProps">code>

{ { headerProps }}

<br />

{ { headerProps.message1 }}

</template>

<!-- 由于子组件没有给slot命名,默认值就为default -->

<template v-slot:default="defaultProps">code>

{ { defaultProps.message2 }}

</template>

</Child>

</template>

渲染结果:

应用场景

作用域插槽在组件封装时非常有用,它能够在保持组件逻辑封装的同时,还能允许父组件根据子组件的数据进行自定义渲染,从而在增强组件复用性和灵活性的同时保持良好的封装性。

我们来看个案例:下面封装了一个列表组件,主要负责渲染展示一个列表,数据由父组件传递。

<code>// 子组件Child

<template>

<div class="child">code>

<h3>{ { title }}</h3>

<p v-for="item in list" :key="item.id">{ { item.id }}.{ { item.text }}</p>code>

</div>

</template>

<script>

export default {

name: "Child",

props: ["title", "list"],

};

</script>

// 父组件Parent(引用子组件Child)

<template>

<Child :list="list" :title="title"> </Child>code>

</template>

<script>

import Child from "./child.vue";

export default {

components: {

Child,

},

data() {

return {

title: "名人名言",

list: [

{

id: 1,

text: "成功就是从失败到失败,也依然不改热情。",

},

{

id: 2,

text: "成为你自己想要看到的改变。",

},

{

id: 3,

text: "努力并不会背叛你,哪怕一时看不到回报。",

},

],

};

},

};

</script>

效果:

但存在一些问题,当有些页面需要复用该组件展示列表时,比如,需要在title处增加一个图标,或者在展示内容时不显示编号1、2、3,就不够灵活。

这时,我们就可以通过作用域插槽,将列表元素内容和样式的控制权交给使用它的父组件来控制

复用该组件时在title处添加图标:

<code>// 子组件Child

<template>

<div class="child">code>

<h3>

<!-- 通过插槽将title传给父组件 -->

<slot name="title" v-bind:titleValue="title"> { { title }} </slot>code>

</h3>

<p v-for="item in list" :key="item.id">code>

<!-- 通过插槽将列表元素传给父组件 -->

<slot name="text" v-bind:itemValue="item"code>

>{ { item.id }}.{ { item.text }}

</slot>

</p>

</div>

</template>

<script>

export default {

name: "Child",

props: ["title", "list"],

};

</script>

<style scoped></style>

// 父组件Parent控制标题的显示(引用子组件Child)

<template>

<Child :list="list" :title="title">code>

<template v-slot:title="slotTitle">code>

<!-- title处添加图标 -->

<a-icon type="smile" />{ { slotTitle.titleValue }}code>

</template>

</Child>

</template>

效果:

展示内容时不显示编号1、2、3:

<code>// 父组件Parent控制列表元素的显示(引用子组件Child)

<template>

<Child :list="list" :title="title">code>

<template v-slot:text="slotItem">code>

{ { slotItem.itemValue.text }}

</template>

</Child>

</template>

效果:

通过作用域插槽,父组件完全控制了如何展示列表项,而不需要修改子组件的内部逻辑。

动态插槽名

2.6.0 新增:动态插槽名

动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:

<code><Child>

<template v-slot:[dynamicSlotName]>

...

</template>

</Child>

这里方括号中的dynamicSlotName会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。

废弃的插槽语法

以上的插槽语法是Vue 2.6.0后的语法,在这之前,插槽语法为slot(默认插槽或具名插槽)和slot-scope(作用域插槽)。

vue官网:v-slot指令自 Vue 2.6.0 起被引入,提供更好的支持 slot 和 slot-scope attribute 的 API 替代方案。在接下来所有的 2.x 版本中 slot 和 slot-scope attribute 仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。

默认插槽

组件省略不写或者在template上使用slot属性:slot="default",将内容从父级传给默认插槽。

<code>// 子组件Child

<template>

<div class="child">code>

<!-- 默认插槽:这个内容会被父组件传递的内容替换 -->

<slot></slot>

</div>

</template>

// 父组件Parent(引用子组件Child)

<template>

<Child>

<template slot="default"> 南木元元 </template>code>

</Child>

</template>

具名插槽

组件在template上使用slot属性:slot="name",将内容从父级传给具名插槽。

<code>// 子组件Child

<template>

<div class="child">code>

<!-- 具名插槽:带name的slot -->

<header>

<slot name="header"></slot>code>

</header>

<main>

<slot></slot>

</main>

<footer>

<slot name="footer"></slot>code>

</footer>

</div>

</template>

// 父组件Parent(引用子组件Child)

<template>

<Child>

<!-- 具名插槽header -->

<template slot="header">code>

<h1>头部标题区</h1>

</template>

<!-- 这是默认插槽 -->

<p>主要内容区</p>

<!-- 具名插槽footer -->

<template slot="footer">code>

<p>底部区域</p>

</template>

</Child>

</template>

作用域插槽

组件在template上使用slot-scope属性,可以接收传递给插槽的 prop。

// 子组件Child

<template>

<div class="child">code>

<header>

<!-- 向具名插槽中传入子组件的数据 -->

<slot name="header" :message1="child1"></slot>code>

</header>

<main>

<!-- 传入子组件的数据,这里没有命名slot -->

<slot :message2="child2"></slot>code>

</main>

</div>

</template>

<script>

export default {

data() {

return {

child1: "消息1",

child2: "消息2",

};

},

};

</script>

// 父组件Parent(引用子组件Child)

<template>

<Child>

<!-- 通过slot-scope的语法,将子组件的message1值赋值给headerProps -->

<template slot="header" slot-scope="headerProps">code>

{ { headerProps.message1 }}

</template>

<!-- 由于子组件没有给slot命名,这里的slot="default"可以忽略为隐性写法 -->code>

<template slot-scope="defaultProps">code>

{ { defaultProps.message2 }}

</template>

</Child>

</template>

效果:

slot-scope 属性也可以直接用于非 <template> 元素 (包括组件

<code><template>

<Child>

<!-- slot-scope attribute 也可以直接用于非 <template> 元素 -->

<span slot-scope="defaultProps">code>

{ { defaultProps.message2 }}

</span>

</Child>

</template>

slot-scope的值可以接收任何有效的可以出现在函数定义的参数位置上的 JavaScript 表达式,即可以在表达式中使用es6解构,这样写法就更加简洁了。

<template>

<Child>

<!-- 使用es6解构 -->

<span slot-scope="{ message2 }">code>

{ { message2 }}

</span>

</Child>

</template>

总结

最后我们来做一个小小的总结:

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~ 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。