在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


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

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

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

相关推荐
文火冰糖的硅基工坊14 分钟前
[嵌入式系统-146]:五次工业革命对应的机器人形态的演进、主要功能的演进以及操作系统的演进
前端·网络·人工智能·嵌入式硬件·机器人
2401_8370885027 分钟前
ResponseEntity - Spring框架的“标准回复模板“
java·前端·spring
yaoganjili35 分钟前
用 Tinymce 打造智能写作
前端
angelQ43 分钟前
Vue 3 中 ref 获取 scrollHeight 属性为 undefined 问题定位
前端·javascript
Dontla1 小时前
(临时解决)Chrome调试避免跳入第三方源码(设置Blackbox Scripts、将目录添加到忽略列表、向忽略列表添加脚本)
前端·chrome
我的div丢了肿么办1 小时前
js函数声明和函数表达式的理解
前端·javascript·vue.js
云中雾丽1 小时前
React.forwardRef 实战代码示例
前端
朝歌青年说1 小时前
一个在多年的技术债项目中写出来的miniHMR热更新工具
前端
武天1 小时前
一个项目有多个后端地址,每个后端地址的请求拦截器和响应拦截器都不一样,该怎么封装
vue.js
Moonbit1 小时前
倒计时 2 天|Meetup 议题已公开,Copilot 月卡等你来拿!
前端·后端