面试官:vue插槽有什么用?插槽的本质是什么?先有问题再有答案 插槽的设计目的是什么? 插槽如何使用? 我们能用插槽做什
先有问题再有答案
插槽的设计目的是什么?
插槽如何使用?
我们能用插槽做什么?
如何理解默认插槽 具名插槽 作用域插槽?
插槽的本质是什么?
为什么插槽不能访问子组件的作用域?
设计目的
插槽 (slot) 特性的设计主要是为了实现组件内容的可重用和内容分发
。这是一种让父组件可以向子组件动态插入内容的特性。
我们可以将任意内容放在组件的标签中,然后在组件的模板中使用 <slot>
标签来决定这些内容的位置。当一个组件渲染的时候, <slot>
将会被替换为它之间的内容。
Vue 插槽的设计可以解决以下需求:
-
内容分发 (Content Projection):插槽可以让开发者在使用一个组件的时候,向组件内部动态传入任意的 DOM 结构。这在开发类似于 Dialog、Card 这样的容器类型组件的时候非常有用。
-
作用域插槽 (Scoped Slot):通过作用域插槽,父组件可以获取到子组件内部的数据,达到更复杂的定制和控制
使用方式
插槽的使用可以分为两部分 首先声明一个插槽 然后传入一个具体的内容替代声明的插槽。
这里使用插槽 实现一个MultColumnList多列列表组件
效果demo
源码
MultColumnList.vue组件
<template>
<div class="list">
<div v-for="(item, index) of props.list" :key="index" :style="itemStyle" class="itemClass">
<slot name="item" :item="item"></slot> // 使用slot标签声明插槽占位
</div>
<div v-for="index of fillList" :key="index" :style="itemStyle" class="itemClass"></div>
</div>
</template>
demo.vue
<template>
<List :list="list" :column="4">
<template #item="{ item }"> // 替换插槽内容
<div class="item">{{ item.name }}</div>
</template>
</List>
</template>
组件完整源码 vue3-mult-column-list
本质 :插槽的本质就是函数
这里使用下面的例子做具体说明。
提供一个Layout组件 可以将页面分为三部分。 header, content, footer。
从上图可以看出 Layout && Layout1 在效果上是等价的。
Layout1实现
<template>
<div class="container">
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
这是我们常规的实现组件的一种方式。
Layout实现
<script>
import { h } from 'vue'
export default {
setup(props,
{ slots }) {
console.log('test slots ', slots)
const header = slots.header();
const content = slots.default();
const footer = slots.footer();
return () => {
return h('div', null, [
...header,
...content,
...footer,
]);
};
},
};
</script>
代码解释:
h函数:h 函数是 Vue.js 中用于创建 VNode 的核心函数,简化了手写渲染函数的过程。它的基本签名为:
h(type, propsOrChildren, children)
,
- type 参数是一个字符串(一个 HTML 元素的标签名)、一个组件,或者是一个异步组件。
- propsOrChildren 参数是一个对象,包含了传递给这个 VNode 的 props,或者是用于设置这个元素的 DOM 属性。如果没有 props,而直接传递了 children,那么这个参数可以是一个子元素或子元素的数组。
- children 参数是 VNode 的子节点,可以是一个字符串(如果是文本节点)、一个 VNode,或者是一个 VNode 数组。
slots对象: slots 用于访问组件传入的内容插槽。
这里打印了slots对象 可以看到slots是一个proxy对象 而且slots里面包含了传入的具体插槽属性 值为一个函数。
插槽属性:
const header = slots.header();
const content = slots.default();
const footer = slots.footer();
这几行代码使用函数调用的方式访问插槽。slots.header()、slots.default() 和 slots.footer() 分别调用了名为 header、默认 (default) 和 footer 的插槽。
可以看到调用插槽函数后返回的具体内容是一个vnode节点数组,这些vnode是可以交个h函数,用于渲染出真实dom节点。
总结
所以插槽的本质就是函数 在回头来看下我们之前实现的列表组件 以函数的视角在带入分析下。
<List :list="list" :column="4">
<template #item="{ item }"> // #item 即为函数名
<div class="item">{{ item.name }}</div> // 函数的返回值
</template>
</List>
#item :插槽名字即为函数名 "{ item }": 所谓的作用域插槽 实际上是函数的参数。 插槽内部的div: 函数的返回值
可以看成如下代码:
<List :list="list" :column="4">
{{slots.item(({item})=>{
return <div class="item">{{ item.name }}</div> })}}
</List>
这也解释了为什么 插槽内容无法访问子组件中的数据了 因为插槽是在父组件调用的函数,所以只能访问父组件的作用域。
转载自:https://juejin.cn/post/7405152755818102835