在Vue中,关于如何在Render函数或JSX中使用具名插槽与作用域插槽

写在开头

插槽Vue 中的一个重要特性,主要有三大部分:默认插槽(基本使用)、具名插槽、作用域插槽。

插槽的基础应用咱们就不扯了,主要来看看在 Render 函数中,是如何使用具名插槽和作用域插槽的,并且有些什么坑呢?👻

基本使用

Child.vue 文件:

javascript 复制代码
<template>
  <div>
    <h1>子组件</h1>
    <slot></slot>
    <slot name="content"></slot>
  </div>
</template>

Parent.vue 文件:

javascript 复制代码
<template>
  <div>
    <h1>父组件</h1>
    <child>
      <div>这是默认插槽内容</div>
      <template v-slot:content>
        <div>这是具名插槽内容</div>
      </template>
    </child>
  </div>
</template>

<script>
import Child from "./Child.vue";

export default {
  components: { Child },
};
</script>

以上是具名插槽的基本应用,接下来看看它们如果转成 Render 函数形式是怎么样的。

Render-JSX

Child.vue 文件:

javascript 复制代码
<script>
export default {
  render() {
    return (
      <div>
        <h1>子组件</h1>
        {this.$slots.default}
        {this.$slots.content}
      </div>
    );
  },
};
</script>

Parent.vue 文件:

javascript 复制代码
<script>
import Child from "./Child.vue";

export default {
  render() {
    return (
      <div>
        <h1>父组件</h1>
        <Child>
          <div>这是默认插槽内容</div>
          <template slot="content">
            <div>这是具名插槽内容</div>
          </template>
        </Child>
      </div>
    );
  },
};
</script>

这里不知道你发现问题没有?

小编使用了一个弃用属性 slot ,从 Vue2.6 版本之后,官方推荐使用 v-slot 替换了旧的 slotslot-scope 属性,虽然它们在 Vue2 中还可以继续使用,但在 Vue3 中将被完整移除,具体可以上官网查看。(这里本来想放个地址的,结果官网又挂了😤)

那么为什么要这么写呢?那肯定是因为,无论我们这样:

javascript 复制代码
<template v-slot:content>
  <div>这是具名插槽内容</div>
</template>

还是这样(GPT 说的一种写法):

xml 复制代码
<Child>
 {{
   content: () => <div>这是具名插槽内容</div>
 }}
</Child>

以上在 JSX 都不行!❌

说明 JSX 中并不支持基本的具名插槽写法,如你知道另外的写法,望评论区告知,感谢。😉

那么,这就完了吗?当然还没,使用弃用属性等于埋下隐患,这里我们可以通过另外的方式来解决,也就是作用域插槽

Child.vue 文件:

javascript 复制代码
<script>
export default {
  render() {
    return (
      <div>
        <h1>子组件</h1>
        {this.$scopedSlots.default()}
        {this.$scopedSlots.content()}
      </div>
    );
  },
};
</script>

Parent.vue 文件:

javascript 复制代码
<script>
import Child from "./Child.vue";

export default {
  render() {
    return (
      <div>
        <h1>父组件</h1>
        <Child
          scopedSlots={{
            default: () => <div>这是默认插槽内容</div>,
            content: () => <div>这是具名插槽内容</div>,
          }}
        />
      </div>
    );
  },
};
</script>

当然,你也可以默认插槽和具名插槽在 JSX 中混着使用,虽然不建议。👻

$slots$scopedSlots 的内容都是由父组件向子组件注入的内容来决定的,它们之间的区别在于 $scopedSlots 可以向上传递数据。

Render-h

以上是关于 Render 函数中 JSX 的插槽内容,但提到 Render 函数肯定就不能不提及 h 函数了,也得来探索一下插槽在其中的使用情况。

Child.vue 文件:

javascript 复制代码
<script>
export default {
  render(h) {
    return h("div", null, [
      h("h1", null, "子组件"),
      this.$scopedSlots.default(),
      this.$scopedSlots.content(),
    ]);
  },
};
</script>

Parent.vue 文件:

javascript 复制代码
<script>
import Child from "./Child.vue";

export default {
  components: { Child },
  render(h) {
    return h("div", null, [
      h("h1", null, "父组件"),
      h(
        "Child",
        {
          scopedSlots: {
            default: () => h("div", null, "这是默认插槽内容"),
            content: () => h("div", null, "这是具名插槽内容"),
          },
        },
      ),
    ]);
  },
};
</script>

折腾一下,你会发现在其中一样不能使用具名插槽,只能通过作用域插槽来实现具名插槽的应用。

不过 h 函数是允许使用默认插槽的,但这其中有一些坑坑洼洼需要注意。

例如:

Child.vue 文件:

javascript 复制代码
<script>
export default {
  render(h) {
    return h("div", null, [
      h("h1", null, "子组件"),
      this.$slots.default,
    ]);
  },
};
</script>

Parent.vue 文件:

javascript 复制代码
<script>
import Child from "./Child.vue";

export default {
  components: { Child },
  render(h) {
    return h("div", null, [
      h("h1", null, "父组件"),
      h("Child", null, "这是默认插槽内容"),
    ]);
  },
};
</script>

// 或者

<script>
import Child from "./Child.vue";

export default {
  components: { Child },
  render(h) {
    return h("div", null, [
      h("h1", null, "父组件"),
      h("Child", null, ["这是默认插槽内容"]), // 数值可以多个
    ]);
  },
};
</script>

// 或者

<script>
import Child from "./Child.vue";

export default {
  components: { Child },
  render(h) {
    return h("div", null, [
      h("h1", null, "父组件"),
      h("Child", null, [h("div", null, "这是默认插槽内容")]),
    ]);
  },
};
</script>

以上写法都可以✔,但是一定不能这样子写:

Parent.vue 文件:

javascript 复制代码
<script>
import Child from "./Child.vue";

export default {
  components: { Child },
  render(h) {
    return h("div", null, [
      h("h1", null, "父组件"),
      h("Child", null, h("div", null, "这是默认插槽内容")),
    ]);
  },
};
</script>

虽然这样子看起来就很合理的,但实际上不行❗

这个问题其实是探究 h 函数第三个参数,那些值能作为默认插槽的内容,具体可以看看源码的判断过程。

源码位置:vue/src/core/vdom/create-element


至此,本篇文章就写完啦,撒花撒花。

希望本文对你有所帮助,如有任何疑问,期待你的留言哦。

老样子,点赞+评论=你会了,收藏=你精通了。

相关推荐
科技探秘人3 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人4 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR9 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香11 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969314 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai19 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_91528 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
Devil枫6 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试