自我介绍:大家好,我是吉帅振的网络日志;微信公众号:吉帅振的网络日志;前端开发工程师,工作4年,去过上海、北京,经历创业公司,进过大厂,现在郑州敲代码。
一、前言
有些时候我们希望子组件模板中的部分内容可以定制化,这个时候使用 Props 就显得不够灵活和易用了。因此,Vue.js 受到 Web Component 草案的启发,通过插槽的方式实现内容分发,它允许我们在父组件中编写 DOM 并在子组件渲染时把 DOM 添加到子组件的插槽中,使用起来非常方便。
二、插槽的用法
<button class="todo-button">
<slot></slot>
</button>
//然后我们在父组件中可以这么使用 TodoButton 组件:
<todo-button>
<!-- 添加一个字体图标 -->
<i class="icon icon-plus"></i>
Add todo
</todo-button>
//其实就是在 todo-button 的标签内部去编写插槽中的 DOM 内容,最终 TodoButton 组件渲染的 HTML 是这样的:
<button class="todo-button">
<!-- 添加一个字体图标 -->
<i class="icon icon-plus"></i>
Add todo
</button>
三、插槽的实现
插槽的特点,其实就是在父组件中去编写子组件插槽部分的模板,然后在子组件渲染的时候,把这部分模板内容填充到子组件的插槽中。父组件渲染阶段,子组件插槽部分的 DOM 是不能渲染的,需要通过某种方式保留下来,等到子组件渲染的时候再渲染。顺着这个思路,我们来分析具体实现的代码。
<layout>
<template v-slot:header>
<h1>{{ header }}</h1>
</template>
<template v-slot:default>
<p>{{ main }}</p>
</template>
<template v-slot:footer>
<p>{{ footer }}</p>
</template>
</layout>
这里你可以借助模板编译工具看一下它编译后的 render 函数:
import { toDisplayString as _toDisplayString, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_layout = _resolveComponent("layout")
return (_openBlock(), _createBlock(_component_layout, null, {
header: _withCtx(() => [
_createVNode("h1", null, _toDisplayString(_ctx.header), 1 /* TEXT */)
]),
default: _withCtx(() => [
_createVNode("p", null, _toDisplayString(_ctx.main), 1 /* TEXT */)
]),
footer: _withCtx(() => [
_createVNode("p", null, _toDisplayString(_ctx.footer), 1 /* TEXT */)
]),
_: 1
}))
}
三、总结
了解插槽的实现原理,知道父组件和子组件在实现插槽 feature 的时候各自做了哪些事情。
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。