【Vue.js 3.0源码】插槽实现内容分发

103 阅读2分钟

自我介绍:大家好,我是吉帅振的网络日志;微信公众号:吉帅振的网络日志;前端开发工程师,工作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 的时候各自做了哪些事情。

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿