[搬运]聊聊Vue.slot原理

2022-11-15  本文已影响0人  薯条你哪里跑

本篇文章给大家分享一下Vue干货,聊聊你可能不知道的Vue.slot原理,希望对大家有所帮助!

image

前端(vue)入门到精通课程:[进入学习](javascript:void(0);)
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API调试工具:点击使用

相信不管是日常业务开发,还是封装基础组件,插槽slot 都是经常出现在我们的视野的,因为它为我们编程实现提供了很多便捷。可能大家对于 slot 的用法已经烂透于心了,不管是 具名插槽 ,还是 作用域插槽 各种用法等等...那大家又知不知道 slotslot-scope 底层是怎么实现的呢?

通俗易懂10分钟就能带走Vue.slot干货源码实现分析!!!跟着笔者一起探究下 Vue(v2.6.14) 中的插槽 slot 是怎么实现的!!本文主要会分两块进行讲解:

这篇文章没有晦涩的源码解析,直接用大白话讲解,所以不管大家对Vue源码的熟悉程度,都是能看明白的。通过现场调试,让你看清 Vueslot 是如何实现的。let's go go go!(学习视频分享:vue视频教程

一、回顾 slot 用法

先跟大家一起回顾下插槽的大概用法。这里的 slot 用法使用 2.6.0 的新标准(本文也会带一下 v2.5 的写法的跟 v2.6 在源码实现上有什么区别!)。

如果想详细了解用法可以去官网详细看看Vue 的 slot 文档

https://cn.vuejs.org/v2/guide/components-slots.html

1. 默认插槽

<!-- 子组件 -->
<template>
  <div class=``"wrapper"``>
  <!-- 默认插槽 -->
  <div class=``"main"``>
    <slot></slot>
  </div>
</template>

<!-- 父组件 -->
<my-slot>
  <template>
    <h1>默认插槽</h1>
  </template>
</my-slot>

页面展示效果如图:

image.png

2. 具名插槽

接着上述的案例,添加具名插槽 header ,代码如下:

<!-- 子组件 -->
<template>
    <div class="wrapper">
        <!-- header 具名插槽 -->
        <header class="header">
            <slot name="header"></slot>
        </header>
        <!-- 默认插槽 -->
        <div class="main">
        <slot></slot>
    </div>
</template>
<!-- 父组件 -->
<my-slot>
    <template v-slot:header>
        <h1>header 具名插槽</h1>
    </template>
    <template>
        <h1>默认插槽</h1>
    </template>
</my-slot>

如上代码块可以发现:

页面展示效果如图:

image.png

3. 作用域插槽(slot-scope)

再接着上述案例,添加作用域插槽 footer ,代码如下

<!-- 子组件 -->
<template>
    <div class="wrapper">
        <!-- header 具名插槽 -->
        <header class="header">
            <slot name="header"></slot>
        </header>
        <!-- 默认插槽 -->
        <div class="main">
            <slot></slot>
        </div>
        <!-- footer 具名 + 作用域插槽 -->
        <footer class="footer">
            <slot name="footer" :footerInfo="footerInfo"></slot>
        </footer>
    </div>
</template>
<script>
export default {
    name: "mySlot",
    data () {
        return {
            footerInfo: {
                text: '这是 子组件 footer插槽 的作用域数据'
            }
        }
    }
}
</script>

<!-- 父组件 -->
<my-slot>
    <template v-slot:header>
        <h1>header 具名插槽</h1>
    </template>
    <template>
        <h1>默认插槽</h1>
    </template>
    <template v-slot:footer="slotProps">
        <h1>footer 具名 + 作用域插槽</h1>
        <p>{{ slotProps.footerInfo.text }}</p>
    </template>
</my-slot>

如上代码块可以发现:

页面展示效果如图:

image.png

好了,简单回顾完用法后,笔者在这里先提三个问题:

  1. 普通插槽、 作用域插槽 的 vNode 是在哪个环节生成的,render 父组件时还是子组件时?
  2. 作用域插槽 为什么能在父组件访问到子组件的数据?
  3. 普通插槽 跟 作用域插槽 在实现上有区别吗?

我们带着疑问接着往下看!

二、不同slot的编译区别

我们根据上述最终的案例代码,执行一下打包命令,看看 Vue 在编译模板的时候,是怎么处理我们的 slot 的!事不宜迟,赶紧 build 一哈~(偷偷告诉大家,Vue 处理 作用域插槽普通插槽 的差异就是从编译开始的,也就是 render函数 会有所不同)

这里笔者顺便使用 v2.5 的具名插槽写法给大家参照一下(对具名插槽header做改写,使用 slot="header" 的写法),大家可以看下 v2.6v2.5 具名插槽的 写法、实现 上的区别~反正也不难,也就顺便带出来看看了

image.png

上图左边是 v2.6 、右边是 v2.5 的,这里,我们集中关注:

其实根据上述编译后的结果,我们不妨这样猜测

这里放出具体的 作用域插槽 打包后代码,大家一看就很清晰了:

{
  scopedSlots: t._u([
    {
      key: "footer",
      // 函数接收了一个参数n   
      fn: function (n) {
      return [
        // h1 标签的 render 函数
        e("h1", [t._v("footer 具名 + 作用域插槽")]),
        // p 标签的 render 函数,这里可以看到编译后是:n.footerInfo.text
        e("p", [t._v(t._s(n.footerInfo.text))])
    ]}
    }
  ])
}

三、slot实现原理

1. 断点调试

为了方便大家看调试结果,当前项目的组件结构主要是这样,有三大层:

Vue -> <App /> -> <my-slot />

这里笔者在运行时代码 initRender()renderSlot() 中,打上 debugger ,直接带大火看看执行流程。这里简单介绍下两个方法:

接下来直接看实验截图:

1、先是进入initRender()(这里跳过初始化 大VueApp 的过程)。直接到初始化 my-slot组件 过程。【 简单解释:由于 App组件 执行 render 得到 App组件vNode ,在 patch 过程中 遇到 vue-component-my-slot 的 vNode ,又执行 my-slot组件 的 初始化流程。不是很熟悉组件化流程的朋友可以去看看笔者的Vue响应式原理~】

image.png image.png

2、再是进入 renderSlot()。接着上面继续单步执行,会走到 renderSlot 中。这时候,已经进入到 my-slot组件render 阶段了。回顾第一步中,此时我们手握 默认插槽的vNode,并存在 vm.$slot.default

header插槽

image.png image.png image.png

默认插槽

image.png image.png

作用域插槽

image.png

2. 总结插槽实现原理

其实上面的流程只是论证过程,大家不可以不必深陷其中。笔者在这里直接根据实践过程,给大伙总结出结论!也就是要回到我们一开始的三个问题!

1、普通插槽、 作用域插槽 的 vNode 是在哪个环节生成的,render 父组件时还是子组件时?

2、作用域插槽 为什么能在父组件访问到子组件的数据?

3、普通插槽 跟 作用域插槽 在实现上有区别吗?

好了,最后来个精炼的总结。作用域插槽一定是延迟执行,且接收参数!普通插槽 可能延迟执行,可能直接执行,但不接收参数!


上一篇 下一篇

猜你喜欢

热点阅读