写在开头
插槽 是 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
,从 Vue
的 2.6
版本之后,官方推荐使用 v-slot
替换了旧的 slot
与 slot-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
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。