在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


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

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

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

相关推荐
A_nanda1 分钟前
根据AI提示排查vue前端项目
前端·javascript·vue.js
happymaker062625 分钟前
web前端学习日记——DAY05(定位、浮动、视频音频播放)
前端·学习·音视频
~无忧花开~36 分钟前
React状态管理完全指南
开发语言·前端·javascript·react.js·前端框架
LegendNoTitle1 小时前
计算机三级等级考试 网络技术 选择题考点详细梳理
服务器·前端·经验分享·笔记·php
@大迁世界1 小时前
1.什么是 ReactJS?
前端·javascript·react.js·前端框架·ecmascript
BJ-Giser2 小时前
Cesium 基于EZ-Tree的植被效果
前端·可视化·cesium
王码码20353 小时前
Flutter for OpenHarmony:Flutter 三方库 algoliasearch 毫秒级云端搜索体验(云原生搜索引擎)
android·前端·git·flutter·搜索引擎·云原生·harmonyos
发现一只大呆瓜3 小时前
深入浅出 AST:解密 Vite、Babel编译的底层“黑盒”
前端·面试·vite
天天鸭3 小时前
前端仔写了个 AI Agent,才发现大模型只干了 10% 的活
前端·python·ai编程
发现一只大呆瓜4 小时前
前端模块化:CommonJS、AMD、ES Module三大规范全解析
前端·面试·vite