0

0

Vue中slot插槽使用详解

php中世界最好的语言

php中世界最好的语言

发布时间:2018-04-20 13:40:41

|

3531人浏览过

|

来源于php中文网

原创

这次给大家带来Vue中slot插槽使用详解,Vue中slot插槽使用的注意事项有哪些,下面就是实战案例,一起来看一下。

下面结合一个例子,简单说明slots的工作原理

dx-li子组件的template如下:

  • 你好!
  • dx-ul父组件的template如下:
      hello juejin!
    结合上述例子以及vue中相关源码进行分析 dx-ul父组件中template编译后,生成的组件render函数: module.exports={ render:function (){ var _vm=this; var _h=_vm.$createElement; var _c=_vm._self._c||_h; // 其中_vm.v为createTextVNode创建文本VNode的函数 return _c('ul', [_c('dx-li', [_vm._v("hello juejin!")])], 1) }, staticRenderFns: [] }

    传递的插槽内容'hello juejin!'会被编译成dx-li子组件VNode节点的子节点。

    渲染dx-li子组件,其中子组件的render函数:

    module.exports={
     render:function (){
      var _vm=this;
      var _h=_vm.$createElement;
      var _c=_vm._self._c||_h;
      // 其中_vm._v 函数为renderSlot函数
      return _c('li', 
        {staticClass: "dx-li" }, 
        [_vm._t("default", [_vm._v("你好 掘金!")])], 
        2
       )
      },
     staticRenderFns: []
    }

    初始化dx-li子组件vue实例过程中,会调用initRender函数:

    function initRender (vm) {
     ...
     // 其中_renderChildren数组,存储为 'hello juejin!'的VNode节点;renderContext一般为父组件Vue实例
     这里为dx-ul组件实例
     vm.$slots = resolveSlots(options._renderChildren, renderContext);
     ...
    }

    其中resolveSlots函数为:

    /**
     * 主要作用是将children VNodes转化成一个slots对象.
     */
    export function resolveSlots (
     children: ?Array,
     context: ?Component
    ): { [key: string]: Array } {
     const slots = {}
     // 判断是否有children,即是否有插槽VNode
     if (!children) {
     return slots
     }
     // 遍历父组件节点的孩子节点
     for (let i = 0, l = children.length; i < l; i++) {
     const child = children[i]
     // data为VNodeData,保存父组件传递到子组件的props以及attrs等
     const data = child.data
     /* 移除slot属性
     *  
     * 编译成span的VNode节点data = {attrs:{slot: "abc"}, slot: "abc"},所以这里删除该节点attrs的slot
     */
     if (data && data.attrs && data.attrs.slot) {
      delete data.attrs.slot
     }
     /* 判断是否为具名插槽,如果为具名插槽,还需要子组件/函数子组件渲染上下文一致。主要作用:
     *当需要向子组件的子组件传递具名插槽时,不会保持插槽的名字。
     * 举个栗子:
     * child组件template: 
     * 

    *

    *

    *

    * parent组件template: * * main组件template: * foo * 此时main渲染的结果: *

    *

    foo

    *

    */ if ((child.context === context || child.fnContext === context) && data && data.slot != null ) { const name = data.slot const slot = (slots[name] || (slots[name] = [])) // 这里处理父组件采用template形式的插槽 if (child.tag === 'template') { slot.push.apply(slot, child.children || []) } else { slot.push(child) } } else { // 返回匿名default插槽VNode数组 (slots.default || (slots.default = [])).push(child) } } // 忽略仅仅包含whitespace的插槽 for (const name in slots) { if (slots[name].every(isWhitespace)) { delete slots[name] } } return slots }

    然后挂载dx-li组件时,会调用dx-li组件render函数,在此过程中会调用renderSlot函数:

    export function renderSlot (
      name: string, // 子组件中slot的name,匿名default
      fallback: ?Array, // 子组件插槽中默认内容VNode数组,如果没有插槽内容,则显示该内容
      props: ?Object, // 子组件传递到插槽的props
      bindObject: ?Object // 针对 obj必须是一个对象
     ): ?Array {
     // 判断父组件是否传递作用域插槽
      const scopedSlotFn = this.$scopedSlots[name]
      let nodes
      if (scopedSlotFn) { // scoped slot
      props = props || {}
      if (bindObject) {
       if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {
       warn(
        'slot v-bind without argument expects an Object',
        this
       )
       }
       props = extend(extend({}, bindObject), props)
      }
      // 传入props生成相应的VNode
      nodes = scopedSlotFn(props) || fallback
      } else {
      // 如果父组件没有传递作用域插槽
      const slotNodes = this.$slots[name]
      // warn duplicate slot usage
      if (slotNodes) {
       if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) {
       warn(
        `Duplicate presence of slot "${name}" found in the same render tree ` +
        `- this will likely cause render errors.`,
        this
       )
       }
       // 设置父组件传递插槽的VNode._rendered,用于后面判断是否有重名slot
       slotNodes._rendered = true
      }
      // 如果没有传入插槽,则为默认插槽内容VNode
      nodes = slotNodes || fallback
      }
      // 如果还需要向子组件的子组件传递slot
      /*举个栗子:
      * Bar组件: 

    * Foo组件:

    * main组件:

    hello

    * 最终渲染:

    hello

    */ const target = props && props.slot if (target) { return this.$createElement('template', { slot: target }, nodes) } else { return nodes } }

    scoped slots理解

    dx-li子组件的template如下:

  • hello juejin!
  • dx-ul父组件的template如下:
      {{scope.str}}
    结合例子和Vue源码简单作用域插槽 dx-ul父组件中template编译后,产生组件render函数: module.exports={ render:function (){ var _vm=this; var _h=_vm.$createElement; var _c=_vm._self._c||_h; return _c('ul', [_c('dx-li', { // 可以编译生成一个对象数组 scopedSlots: _vm._u([{ key: "default", fn: function(scope) { return _c('span', {}, [_vm._v(_vm._s(scope.str))] ) } }]) })], 1) }, staticRenderFns: [] }

    其中 _vm._u函数:

    function resolveScopedSlots (
     fns, // 为一个对象数组,见上文scopedSlots
     res
    ) {
     res = res || {};
     for (var i = 0; i < fns.length; i++) {
      if (Array.isArray(fns[i])) {
       // 递归调用
       resolveScopedSlots(fns[i], res);
      } else {
       res[fns[i].key] = fns[i].fn;
      }
     }
     return res
    }

    子组件的后续渲染过程与slots类似。scoped slots原理与slots基本是一致,不同的是编译父组件模板时,会生成一个返回结果为VNode的函数。当子组件匹配到父组件传递作用域插槽函数时,调用该函数生成对应VNode。

    总结

    其实slots/scoped slots 原理是非常简单的,我们只需明白一点vue在渲染组件时,是根据VNode渲染实际DOM元素的。

    slots是将父组件编译生成的插槽VNode,在渲染子组件时,放置到对应子组件渲染VNode树中。

    scoped slots是将父组件中插槽内容编译成一个函数,在渲染子组件时,传入子组件props,生成对应的VNode。最后子组件,根据组件render函数返回VNode节点树,update渲染真实DOM元素。同时,可以看出跨组件传递插槽也是可以的,但是必须注意具名插槽传递。

    相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

    推荐阅读:

    vue组件的slot插口使用详解

    AITDK
    AITDK

    免费AI SEO工具,SEO的AI生成器

    下载

    JS同源策略+跨域访问使用详解

    立即学习前端免费学习笔记(深入)”;

    相关专题

    更多
    php文件怎么打开
    php文件怎么打开

    打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

    1706

    2023.09.01

    php怎么取出数组的前几个元素
    php怎么取出数组的前几个元素

    取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

    1138

    2023.10.11

    php反序列化失败怎么办
    php反序列化失败怎么办

    php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

    1038

    2023.10.11

    php怎么连接mssql数据库
    php怎么连接mssql数据库

    连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

    948

    2023.10.23

    php连接mssql数据库的方法
    php连接mssql数据库的方法

    php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

    1396

    2023.10.23

    html怎么上传
    html怎么上传

    html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

    1228

    2023.11.03

    PHP出现乱码怎么解决
    PHP出现乱码怎么解决

    PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

    1439

    2023.11.09

    php文件怎么在手机上打开
    php文件怎么在手机上打开

    php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

    1303

    2023.11.13

    虚拟号码教程汇总
    虚拟号码教程汇总

    本专题整合了虚拟号码接收验证码相关教程,阅读下面的文章了解更多详细操作。

    25

    2025.12.25

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    Go 教程
    Go 教程

    共32课时 | 3万人学习

    麻省理工大佬Python课程
    麻省理工大佬Python课程

    共34课时 | 4.9万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号