ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

VUE render 函数中使用插槽、具名插槽与作用域插槽

2021-03-08 14:59:28  阅读:638  来源: 互联网

标签:VUE 作用域 插槽 具名 prop content other 组件


1、Vue render

Vue render是Vue在编译模板时的必经之路,通过 template 形式写出的模板最终会经由 render 函数渲染到dom上,正如 render 介绍所说的

然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器

如果你还不了解 render 函数的作用以及能力,建议你现在官网了解一下 :)
Vue官网 render函数介绍

2、slots

如果你还不了解 slot 的作用以及能力,建议你现在官网了解一下 :)
Vue官网 插槽、具名插槽、作用域插槽介绍

slot(插槽)是 父组件 向 子组件 提供内容的一种方式,下面贴出代码

// 父组件使用 child 组件
<child></child>

// 子组件内容
<template>
  <div>
    我是子组件!
    <slot>我是默认插槽的后备内容</slot>
    <br>
    <slot name="content">我是具名插槽 content 的后备内容</slot>
    <br>
    <slot name="other">我是具名插槽 other 的后备内容</slot>
  </div>
</template>

这样对应的结果是

我是子组件! 我是默认插槽的后备内容
我是具名插槽 content 的后备内容
我是具名插槽 other 的后备内容

我们先来看看子组件对应的 $slots ==> {}
是一个空对象,这是由于 现在父组件部分还没有为子组件提供插槽的内容

如果父组件形如

	<child>
      <template v-slot:default>
        我是父组件提供的默认内容
      </template>
      <template v-slot:content>
        我是父组件提供的 content
      </template>
      <template v-slot:other>
        我是父组件提供的 other
      </template>
    </child>

则渲染结果为

我是子组件!
我是父组件提供的默认内容
我是父组件提供的 content
我是父组件提供的 other

子组件对应的 $slot为

{
content:
	[VNode {tag: undefined, data: undefined, children: undefined, text: " 我是父组件提供的 content ", elm: undefined, …}]
default:
	[VNode {tag: undefined, data: undefined, children: undefined, text: " 我是父组件提供的默认插槽 ", elm: undefined, …}]
other:
	[VNode {tag: undefined, data: undefined, children: undefined, text: " 我是父组件提供的 other ", elm: undefined, …}]
}

可以看到, 子组件中的 $slot 将父组件提供的内容传了过来,
$slot 对象中的每个属性对应一个具名插槽(slot的名称为 default),它的内容则是父组件提供的模板经由编译之后的 VNode 对象,这个VNode对象后续会渲染为对应的内容

注意! 父组件再没有书写 v-slot 时子组件的 $slots 为空!因此再编写子组件渲染函数的时候注意检查

3、scopedSlots

同样使用上面的组件,这次稍微修改些内容

  <template>
  <div>
    我是子组件!
    <br>    
    
    <!-- 各新增一个 text 属性 -->
    <slot text="我是默认插槽">我是默认插槽的后备内容</slot>
    <br>
    <slot name="content" text="我是具名插槽content">我是具名插槽 content 的后备内容</slot>
    <br>
    <slot name="other" text="我是具名插槽other">我是具名插槽 other 的后备内容</slot>
  </div>
</template>

父组件通过作用域插槽获取到子组件暴露的内容

<child>
      <template v-slot:default="prop">
        {{ prop }}
      </template>
      <template v-slot:content="prop">
        {{ prop }}
      </template>
      <template v-slot:other="prop">
        {{ prop }}
      </template>
    </child>

这是的效果是

我是子组件!
{ "text": "我是默认插槽" }
{ "text": "我是具名插槽content" }
{ "text": "我是具名插槽other" }

子组件中的 $scopedSlots 的内容是

{
	content: ƒ ()
	default: ƒ ()
	other: ƒ ()
	$hasNormal: false
	$key: undefined
	$stable: true
}

可以看到,这个对象中出现了和具名插槽名称一致的三个函数 content default 和 other
这三个函数可以接收一个参数作为要给父组件使用的对象,并返回[VNode]
换句话说,v-slot:content="prop" 中的 prop 就是 $scopedSlots.content(prop)这个参数给的

注意! 父组件再没有书写 v-slot 时子组件的 $scopedSlots 没有上述函数!因此再编写子组件渲染函数的时候注意检查

在 Vue API 中了解更多

4、在 render 函数中的使用 $slots/$scopedSlots

有了上面内容的铺垫,可以看到,不论是 $slots 还是 $scopedSlots ,它们的属性都是父组件向子组件注入的内容决定的,只不过 $scopedSlots可以再向父组件抛出数据

它们是在模板上编写 <slot> 后 Vue 替你进行的下一步操作。
现在我们在 render 上自己执行它们

第一个 slots 的例子来改,可以改为

<template>
  <div>
    我是子组件!
    <slot>我是默认插槽的后备内容</slot>
    <br>
    <slot name="content">我是具名插槽 content 的后备内容</slot>
    <br>
    <slot name="other">我是具名插槽 other 的后备内容</slot>
  </div>
</template>
--------------------------------------------变为了-------------------------------------------
render(_c) {
    let def = this.$slots.default;
    def || (def = "我是默认插槽的后备内容");

    let con = this.$slots.content;
    con || (con = "我是具名插槽 content 的后备内容");

    let oth = this.$slots.other;
    oth || (oth = "我是具名插槽 other 的后备内容");
    return _c("div", [
      "我是子组件!",
      _c("br"),
      def,
      _c("br"),
      con,
      _c("br"),
      oth,
    ]);
  },

父组件提供内容、父组件不提供内容的效果依次为:

我是子组件!
我是父组件提供的默认内容
我是父组件提供的 content
我是父组件提供的 other

我是子组件!
我是默认插槽的后备内容
我是具名插槽 content 的后备内容
我是具名插槽 other 的后备内容

对于 $scopedSlots,使用起来也大同小异

<template>
  <div>
    我是子组件!
    <br>    
    <slot text="我是默认插槽"></slot>
    <br>
    <slot name="content" text="我是具名插槽content"></slot>
    <br>
    <slot name="other" text="我是具名插槽other"></slot>
  </div>
</template>
--------------------------------------------变为了-------------------------------------------
---------------注意! 示例没有进行类型检查,但是开发时无法保证所有的插槽正确、完整书写,---------
---------------需要类型检查或提供默认值,见下方注释--------------------------------------------
render(_c) {
    // 如果要提供后备内容可以通过 判断 this.$scopedSlots.xxx 不为函数后渲染默认内容等方式
    // 这里省略
    let def = this.$scopedSlots.default({
      text: "我是默认插槽",
    });

    let con = this.$scopedSlots.content({
      text: "我是具名插槽content",
    });

    let oth = this.$scopedSlots.other({
      text: "我是具名插槽other",
    });
    return _c("div", [
      "我是子组件!",
      _c("br"),
      def,
      _c("br"),
      con,
      _c("br"),
      oth,
    ]);
  },

效果和 $scopedSlots 部分的结果一致

我是子组件!
{ "text": "我是默认插槽" }
{ "text": "我是具名插槽content" }
{ "text": "我是具名插槽other" }

5、使用 scopedSlots

它不同于 $scopedSlots,它是存在于 render数据对象 中的一个对象
效果是获取 子组件向父组件 抛出的内容而不是抛出内容给父组件

就以刚刚的父组件为例!

<child>
    <template v-slot:default="prop">
      {{ prop }}
    </template>
    <template v-slot:content="prop">
      {{ prop }}
    </template>
    <template v-slot:other="prop">
      {{ prop }}
    </template>
 </child>
 --------------------------------------------变为了-------------------------------------------
 ---------------注意! 示例没有进行类型检查,但是开发时无法保证所有的插槽正确、完整书写,-----------
 render(_c) {
    return _c("child", {
      scopedSlots: {
        default(prop) {
          // 对应 v-slot:default="prop"
          return prop;
        },
        content(prop) {
          // 对应 v-slot:default="prop"
          return prop;
        },
        other(prop) {
          // 对应 v-slot:default="prop"
          return prop;
        },
      },
    });
  },

结果一致:

我是子组件!
{ "text": "我是默认插槽" }
{ "text": "我是具名插槽content" }
{ "text": "我是具名插槽other" }

6、总结

简单来说:
v-slot:name ====> 子组件 this.$slots.name
v-slot 或者单纯的<slot>标签 ====> 子组件 this.$slots.default
v-slot="xxx" ====> 子组件抛出this.$scopedSlots.default(xxx)/ 父组件使用scoepdSlots: { default(xxx){ return VNode } }
v-slot:name="xxx" ====> 子组件抛出this.$scopedSlots.name(xxx)/ 父组件使用scoepdSlots: { name(xxx){ return VNode } }

7、实践

实践是检验真理的唯一标准,如果你了解 element-ui 的话,可以到我的 github 上查看一个以面向对象的形式创建简单表格的工具,在源码中有使用这三者的部分。⬇
小工具

标签:VUE,作用域,插槽,具名,prop,content,other,组件
来源: https://blog.csdn.net/ruienger/article/details/114526311

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有