在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


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

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

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

相关推荐
吴永琦(桂林电子科技大学)15 分钟前
HTML5
前端·html·html5
爱因斯坦乐17 分钟前
【HTML】纯前端网页小游戏-戳破彩泡
前端·javascript·html
恋猫de小郭23 分钟前
注意,暂时不要升级 MacOS ,Flutter/RN 等构建 ipa 可能会因 「ITMS-90048」This bundle is invalid 被拒绝
android·前端·flutter
大莲芒4 小时前
react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析--react17
前端·react.js·前端框架
木木黄木木6 小时前
html5炫酷3D文字效果项目开发实践
前端·3d·html5
Li_Ning217 小时前
【接口重复请求】axios通过AbortController解决页面切换过快,接口重复请求问题
前端
胡八一7 小时前
Window调试 ios 的 Safari 浏览器
前端·ios·safari
Dontla7 小时前
前端页面鼠标移动监控(鼠标运动、鼠标监控)鼠标节流处理、throttle、限制触发频率(setTimeout、clearInterval)
前端·javascript
再学一点就睡8 小时前
深拷贝与浅拷贝:代码世界里的永恒与瞬间
前端·javascript
CrimsonHu8 小时前
B站首页的 Banner 这么好看,我用原生 JS + 三大框架统统给你复刻一遍!
前端·javascript·css