Appearance
33.Vue 中 slot 是如何实现的?什么时候使用它?
1.1 什么是插槽?
插槽设计来源于 Web Components 规范草案,利用slot
进行占位,在使用组件时,组件标签内部内容会分发到对应的 slot 中。
1.2 什么时候使用它
通过插槽可以让用户更好的对组件进行扩展和定制化。可以通过具名插槽指定渲染的位置。常用的组件例如:弹框组件、布局组件、表格组件、树组件......
1.3 插槽的分类和原理
具名插槽,作用域插槽
1)Vue2 插槽实现
普通插槽的实现
vue<template> <my> <h1 slot="title">标题</h1> <div slot="content">内容</div> </my> </template> <!-- 编译后的结果 with(this){ return _c('my',[ _c('h1',{attrs:{"slot":"title"},slot:"title"},[_v("标题")]) _c('div',{attrs:{"slot":"content"},slot:"content"},[_v("内容")]) ]) } -->
<template> <my> <h1 slot="title">标题</h1> <div slot="content">内容</div> </my> </template> <!-- 编译后的结果 with(this){ return _c('my',[ _c('h1',{attrs:{"slot":"title"},slot:"title"},[_v("标题")]) _c('div',{attrs:{"slot":"content"},slot:"content"},[_v("内容")]) ]) } -->
my.vue
vue<template> <div> <slot name="title"></slot> <slot name="content"></slot> </div> </template> <!-- 编译后的结果 with(this){ return _c('div',[ _t("title"),_t("content") ]) } -->
<template> <div> <slot name="title"></slot> <slot name="content"></slot> </div> </template> <!-- 编译后的结果 with(this){ return _c('div',[ _t("title"),_t("content") ]) } -->
作用域插槽
vue<template> <my> <template v-slot="{ article }"> <h1>{{ article.title }}</h1> <div>{{ article.content }}</div> </template> </my> </template> <!-- with(this) { return _c('my', { scopedSlots: _u([ { key: "default", fn: function ({ article }) { return [_c('h1', [_v(_s(article.title))]), _c('div',[ _v(_s(article.content)) ])] } } ]) }) } -->
<template> <my> <template v-slot="{ article }"> <h1>{{ article.title }}</h1> <div>{{ article.content }}</div> </template> </my> </template> <!-- with(this) { return _c('my', { scopedSlots: _u([ { key: "default", fn: function ({ article }) { return [_c('h1', [_v(_s(article.title))]), _c('div',[ _v(_s(article.content)) ])] } } ]) }) } -->
vue<template> <div> <slot :article="{ title: '标题', content: '内容' }"></slot> </div> </template> <!-- 编译后的结果 with(this){ return _c('div',[_t("default",null,{"article":{title:'标题',content:'内容'}})]) } -->
<template> <div> <slot :article="{ title: '标题', content: '内容' }"></slot> </div> </template> <!-- 编译后的结果 with(this){ return _c('div',[_t("default",null,{"article":{title:'标题',content:'内容'}})]) } -->
普通插槽,渲染在父级, 作用域插槽在组件内部渲染!
2)Vue3 插槽实现
vue
<template>
<my>
<template #default="{ article }">
<h1>{{ article.title }}</h1>
<div>{{ article.content }}</div>
</template>
</my>
</template>
<!--
export function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_my = _resolveComponent("my")
return (_openBlock(), _createBlock(_component_my, null, {
default: _withCtx(({ article }) => [
_createElementVNode("h1", null, _toDisplayString(article.title), 1 /* TEXT */),
_createElementVNode("div", null, _toDisplayString(article.content), 1 /* TEXT */)
]),
}))
}
-->
<template>
<my>
<template #default="{ article }">
<h1>{{ article.title }}</h1>
<div>{{ article.content }}</div>
</template>
</my>
</template>
<!--
export function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_my = _resolveComponent("my")
return (_openBlock(), _createBlock(_component_my, null, {
default: _withCtx(({ article }) => [
_createElementVNode("h1", null, _toDisplayString(article.title), 1 /* TEXT */),
_createElementVNode("div", null, _toDisplayString(article.content), 1 /* TEXT */)
]),
}))
}
-->
vue
<template>
<div>
<slot :article="{ title: '标题', content: '内容' }"></slot>
</div>
</template>
<!--
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_renderSlot(_ctx.$slots, "default", { article: { title: '标题', content: '内容' } })
]))
}
-->
<template>
<div>
<slot :article="{ title: '标题', content: '内容' }"></slot>
</div>
</template>
<!--
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_renderSlot(_ctx.$slots, "default", { article: { title: '标题', content: '内容' } })
]))
}
-->