• 技术文章 >web前端 >Vue.js

    聊聊Vue.slot原理,起探究下slot 是怎么实现的!

    青灯夜游青灯夜游2022-05-10 11:55:03转载489
    本篇文章给大家分享一下Vue干货,聊聊你可能不知道的Vue.slot原理,希望对大家有所帮助!

    相信不管是日常业务开发,还是封装基础组件,插槽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>

    页面展示效果如图:

    1.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>

    如上代码块可以发现:

    页面展示效果如图:

    2.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>

    如上代码块可以发现:

    页面展示效果如图:

    3.png

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

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

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

    二、不同slot的编译区别

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

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

    4.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响应式原理~】

    5.png

    6.png

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

    header插槽

    7.png

    8.png

    9.png

    默认插槽

    10.png

    11.png

    作用域插槽

    12.png

    2. 总结插槽实现原理

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

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

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

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

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


    写在最后,很多时候我们搬砖,遵照文档把功能实现确实省力省心~但当你做多了,你就发现当前的东西缺乏挑战,索然无味。那这个时候,就会有一种冲动,想深入其实现原理,看看 slot 到底是怎么实现的。特别是作用域插槽。用的时候都会想当然的觉得在上层组件通过作用域插槽拿到子组件的数据理所应当,但是在深入源码之后,看懂了别人是怎么做的,会有突然恍然大悟的感觉~

    (学习视频分享:web前端开发编程基础视频

    以上就是聊聊Vue.slot原理,起探究下slot 是怎么实现的!的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除
    专题推荐:Vue vue.js
    上一篇:聊聊vue指令中的修饰符,常用事件修饰符总结 下一篇:一文解析Vue中的插槽(slot)
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【腾讯云】年中优惠,「专享618元」优惠券!• 深入浅析vue3+vite中怎么使用svg图标• 收藏这些vue项目性能优化方式,总有一天能用上!• 5 款适合国内使用的 Vue 移动端 UI 组件库• 一文了解Vue3中的watchEffect,聊聊其应用场景!• 通过9个Vue3 组件库,看看聊前端的流行趋势!• 聊聊vue指令中的修饰符,常用事件修饰符总结
    1/1

    PHP中文网