目录
插槽使用
插槽(Slot)是Vue为组件封装者提供的能力,允许开发者在封装组件时,将不确定的、希望由用户指定的部分定义为插槽。其核心作用是让父组件可以向子组件指定位置插入HTML结构 ,是一种组件间通信方式,适用于父组件向子组件传递内容的场景。例如,当多个组件框架结构相同但内部内容不同(如分类栏中的电影列表和风景图片)时,可通过插槽灵活定制内容
Vue 2 中,插槽(slots)是组件之间传递内容的一种方式。它们允许你在父组件中定义要插入到子组件中的内容。Vue 2 提供了三种类型的插槽:默认插槽、具名插槽和作用域插槽。
默认插槽
默认插槽是没有指定名称的插槽,通常用于当只有一个插槽出口时。以下是如何使用默认插槽的例子:
<!-- 子组件模板 -->
<template>
<div>
<slot>这里是默认内容</slot>
</div>
</template>
<!-- 父组件使用子组件 -->
<child-component>
<span>这是通过父组件传入的内容。</span>
</child-component>
如果没有提供任何内容,将会渲染默认内容"这里是默认内容"。
具名插槽
如果你需要在同一组件中有多个插槽出口,你可以为每个插槽指定一个名字。这样你就可以有针对性地向特定的插槽传递内容:
<!-- 子组件模板 -->
<template>
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父组件使用子组件 -->
<child-component>
<template v-slot:header>
<h1>这里会出现在头部</h1>
</template>
<p>这里会出现在主体部分</p>
<template #:footer>
<p>这里会出现在页脚</p>
</template>
</child-component>
父组件使用slot="名称"
(Vue2.6前)或v-slot:名称
(Vue2.6+,简写为#名称
)指定内容插入位置,推荐使用<template>
标签包裹以避免多余DOM结构
v-slot
是 Vue 2.6.0 引入的指令,它取代了旧的 slot
属性。为了简化代码,可以使用 #
作为 v-slot:
的缩写。
2.6以前案例
<!-- 子组件模板 -->
<template>
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<!-- 父组件使用子组件 -->
<child-component>
<template slot="header">
<h1>这里会出现在头部</h1>
</template>
<p>这里会出现在主体部分</p>
<template slot="footer">
<p>这里会出现在页脚</p>
</template>
</child-component>
作用域插槽
有时你可能希望从子组件向父组件传递数据。这时可以使用作用域插槽,通过插槽 prop 将数据从子组件传递给父组件:
<!-- 子组件模板 -->
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { firstName: 'John', lastName: 'Doe' }
};
}
};
</script>
<!-- 父组件使用子组件 -->
<child-component>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }} {{ slotProps.user.lastName }}
</template>
</child-component>
在这个例子中,slotProps
包含了所有由子组件传递过来的插槽 prop,父组件可以通过解构的方式直接访问这些属性。
Vue 2.6 之前,slot-scope
是实现作用域插槽的主要方式
<!-- 子组件模板 -->
<template>
<div>
<!-- 向父组件传递一个名为 'user' 的对象 -->
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: 'John Doe', age: 30 }
};
}
};
</script>
<!-- 父组件使用子组件 -->
<child-component>
<!-- 使用 slot-scope 来接收从子组件传来的数据 -->
<template slot-scope="slotProps">
{{ slotProps.user.name }} - {{ slotProps.user.age }}
</template>
</child-component>
父组件插槽里的内容是响应式的情况解析
父组件数据变化触发重新渲染
- 当父组件
v-slot
中使用的响应式数据发生变化 时(如data
或computed
属性),Vue 的响应式系统会自动触发父组件的重新渲染。 - 父组件会重新生成插槽内容(模板内容),包括作用域插槽中定义的 DOM 结构和逻辑。
插槽内容更新传递到子组件
- 重新生成的插槽内容(含更新后的数据)会作为新的 VNode 传递给子组件。
- 子组件通过
<slot>
接收更新后的内容,并替换旧内容,实现动态插入。
子组件的更新策略
- 不触发子组件自身渲染 :
若子组件内部状态未变化,仅插槽内容更新,则子组件不会触发自身重渲染(节省性能)。 - 仅局部更新 :
Vue 通过虚拟 DOM 的 diff 算法,仅更新插槽对应的 DOM 节点,而非整个子组件
示例说明
假设有一个场景,父组件包含一个响应式的计数器,该计数器用于控制作用域插槽中显示的内容:
<!-- 子组件 -->
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: 'John Doe' }
};
}
};
</script>
<!-- 父组件 -->
<template>
<div>
<child-component>
<template v-slot:default="slotProps">
{{ slotProps.user.name }} - Counter: {{ counter }}
</template>
</child-component>
<button @click="increment">Increment Counter</button>
</div>
</template>
<script>
export default {
data() {
return {
counter: 0
};
},
methods: {
increment() {
this.counter++;
}
}
};
</script>
在这个例子中,每当点击按钮时,counter
值增加,并且由于 counter
是响应式的,Vue 会自动更新插槽中显示的内容。注意这里虽然 counter
是父组件的数据,但其变化会导致插槽内容的更新,而无需重新渲染整个子组件。
结论
- 当父组件
v-slot
内使用的数据是响应式的并且发生变化时,Vue 会自动重新渲染受影响的部分。 - 插槽内容基于父组件的数据进行渲染,并通过插槽机制放置在子组件模板的指定位置。因此,更新是局部的,只影响到相关内容,而不是整个子组件。
- 这种机制保证了高效的更新策略,使得应用性能最优。
底层原理解析(如果有不正确,欢迎大佬指正)
关键机制与技术原理
-
作用域插槽的本质
- 作用域插槽实际是函数式组件 :
子组件的<slot>
会被编译为一个函数,父组件传递的插槽内容作为该函数的参数。 - 数据流:
子组件数据 → 通过函数参数传递 → 父组件模板渲染 → 结果插入子组件
- 作用域插槽实际是函数式组件 :
-
响应式依赖追踪
- 父组件在渲染插槽时,会自动追踪所有响应式依赖 (如
slotProps.item.text
)。 - 当依赖数据变化时,Vue 精准触发插槽内容的重新渲染。
- 父组件在渲染插槽时,会自动追踪所有响应式依赖 (如
-
性能优化策略
- 静态内容提升 :
若插槽中存在静态内容(如<div>标题</div>
),Vue 会跳过其重复渲染。 - 动态内容隔离 :
动态部分(如{``{slotProps.item}}
)独立更新,避免整树重渲染
- 静态内容提升 :
插槽中的样式处理
在 Vue 中,插槽(slot)的内容是由父组件定义的,因此插槽中的样式通常遵循父组件的样式规则。这意味着父组件可以直接控制插槽内容的样式,而无需子组件特别处理。
综合案例:
假设我们有一个子组件 CardDemo.vue
,它定义了一个卡片布局,并且允许父组件通过作用域插槽自定义卡片的内容。我们的目标是确保父组件传递的内容能够正确地应用样式。
子组件:CardDemo.vue
<template>
<div class="card">
<header class="card-header">
<slot name="header" >Default Header</slot>
</header>
<main class="card-body">
<slot >Default Body Content</slot>
</main>
</div>
</template>
<script>
export default {
data() {
return {
};
}
};
</script>
<style scoped>
.card {
border: 1px solid #ccc;
border-radius: 4px;
padding: 16px;
}
.card-header {
background-color: #f7f7f7;
padding-bottom: 8px;
margin-bottom: 16px;
}
.card-body {
color: #333;
}
</style>
在这个例子中,Card.vue
组件有两个插槽:一个具名插槽 header
和一个默认插槽。
父组件:App.vue
接下来,在父组件中,我们将使用 CardDemo.vue
并为插槽内容应用样式。
<template>
<div id="app">
<card-demo>
<template v-slot:header>
<h2 class="card-title">Custom Header</h2>
</template>
<template v-slot:default>
<p class="card-text">This is a custom paragraph inside the card.</p>
</template>
</card-demo>
</div>
</template>
<script>
import CardDemo from './components/CardDemo.vue';
export default {
components: {
CardDemo
}
};
</script>
<style scoped>
/* 父组件的样式 */
.card-title {
color: red;
}
.card-text {
font-weight: bold;
color:blue;
}
</style>

写在最后.
能入手一个可以的项目. 框架是基础. 底层的js+html+css 也需要打牢.
博主也完结了 前端 基础课程 .参考 https://blog.csdn.net/chxii/category_12913839.html