说说你对slot的理解?slot使用场景有哪些?

本文详细介绍了Vue中的slot机制,包括slot的定义、使用场景、分类及工作原理。通过实例解析了默认插槽、具名插槽和作用域插槽的用法,帮助开发者更好地理解和应用组件的扩展与定制。

在这里插入图片描述

一、slot是什么

在HTML中slot元素,作为Web Components技术套件的一部分,是Web组件内的一个占位符

该占位符可以在后期使用自己的标记语言填充

举个栗子

<template id="element-details-template">
	<slot name="element-name">Slot template</slot>
</template>
<element-details>
	<span slot="element-name">1</span>
</element-details>
<element-details>
	<span slot="element-name">2</span>
</element-details>

template不会展示到页面中,需要用先获取它的引用,然后添加到DOM中

customElements.define('element-details',
	class extends HTMLElement{
		constructor(){
			super();
			const template = document
				.getElementById('element-details-template')
				.content;
			const shadowRoot = this.attachShadow({mode:'open'})
				.appendChild(template.cloneNode(true))			  										
	}
})

在Vue中的概念也是如此

Slot艺名插槽,花名"占坑",我们可以理解为slot在组件模板中占好了位置,当使用该组件标签时候,组件标签里面的内容就会自动填充(替换组件模板中slot位置),作为承载分发内容的出口

可以将其类比为插卡式的FC游戏机,游戏机暴露卡槽(插槽)让用户插入不同的游戏磁条(自定义内容)

二、使用场景

通过插槽可以让用户可以拓展组件,去更好地复用组件和对其做定制化处理

如果父组件在使用到一个复用组件的时候,获取这个组件在不同地方有少量的更改,如果去重写组件是一件不明智的事情

通过slot插槽向组件内部指定位置传递内容,完成这个复用组件在不同场景的应用

比如布局组件、表格列、下拉选、弹框显示内容等

三、分类

slot可以分为一下三种:

  • 默认插槽
  • 具名插槽
  • 作用域插槽

默认插槽
子组件用< slot >标签来确定渲染位置,标签里面可以放DOM结构,当父组件使用的时候没有往插槽传入内容,标签内DOM结构就会显示在页面

父组件在使用的时候,直接在子组件的标签内写入内容即可

子组件Child.vue

<template>
	<slot>
		<p>插槽后备的容</p>
	</slot>
</template>

父组件

<Child>
	<div>默认插槽</div>
</Child>

具名插槽
子组件用name属性来表示插槽的名字,不传为默认插槽

父组件中在使用时在默认插槽的基础上加上slot属性,值为子组件插槽name属性值

子组件Child.vue

<template>
	<slot>插槽后备的内容</slot>
	<slot name="content">插槽后备的内容</slot>
</tamplate>

父组件

<Child>
	<template v-slot:default>具名插槽
</template>
	<!--具名插槽 插槽名做参数-->
	<template v-slot:content>内容....</template>
</Child>

作用域插槽
子组件在作用域上绑定属性来将自组件的信息传给父组件使用,这些属性会被挂在父组件v-slot接收的对象上

父组件中在使用时通过 v-slot: (简写:#)获取子组件的信息,在内容中使用

子组件Child.vue

<template>
	<slot name="footer" testProps="子组件的值">
		<h3>没传footer插槽</h3>
</slot>
</template>

父组件

<Child>
	<!--把v-slot的值指定为作用域上下文对象-->
	<template v-slot:default="slotProps">
		来自子组件数据:{{slotProps.testProps}}
</template>
	<template #default="slotProps">
		来自子组件数据:{{slotProps.testProps}}
</template>
</Child>

小结:

  • v-slot属性只能在< template >上使用,但在只有默认插槽时可以在组加标签上使用
  • 默认插槽名为 default ,可以省略 default 直接写 v-slot
  • 缩写为 # 时不能不写参数,写成 #default
  • 可以通过解构获取 v-slot={user} ,还可以重命名 v-slot="{user:newName}" 和定义默认值 v-slot="{user = ‘默认值’}"

四、原理分析

slot 本质上时返回 VNode 的函数,一般情况下, Vue 中的组件要渲染到页面上需要经过 template -> render function -> VNode -> DOM 过程,这里看看 slot 如何实现:

编写一个 buttonCounter 组件,使用匿名插槽

Vue.component('button-counter',{
	template:'<div><slot>我是默认内容</slot></div>'
})

使用该组件

new Vue({
	el:"#app",
	template:"<button-counter><span>我是slot传入的内容</span></button-counter>",
	components:{buttonCounter}
})

获取 buttonCounter 组件渲染函数

(function anonymous){
	with(this){return _c('div',[_t("default",[_v('我是默认内容')])],2)}
}

_v 表示创建普通文本节点,_t 表示渲染插槽的函数

渲染插槽函数 renderSlot (做了简化)

function renderSlot(name,fallback,props,bindObject){
	//得到渲染插槽内容的函数
	var scopedSlotFn = this.$scopedSlots[name];
	var nodes;
	//如果存在插槽渲染函数,则执行插槽渲染函数,生成nodes节点返回
	//否则使用默认值
	nodes = scopedSlotFn(props) || fallback;
	return nodes;
}

name 属性表示定义插槽的名字,默认值为 default ,fallback 表示子组件中的 slot 节点的默认值

关于 this.$scopedSlots 是什么,我们可以先看看 vm.slot

function initRender(vm){
	...
	vm.$slots = resolveSlots(options._renderChildren,renderContext);
}

resolveSlots 函数会对 children 节点做归类和过滤处理,返回 slots

function resolveSlots(children,context){
	if(!children || !children.length){
		return {}
	}
	var slots = {};
	for(var i = 0,l = children.length;i < l;i++){
		var child = children[];
		var data = child.data;
		//remove slot attribute if the node is resolved as a Vue slot node
		if(data && data.attrs && data.attrs.slot){
			delete data.attrs.slot;
		}
		//named slots should noly be respected if the vnode was rendered in the same context
		if((child.context === context || child.fnContext === context) && data && data.slot != null){
			//如果slot存在(slot="header")则拿对应的值作为key
			var name = data.slot;
			var slot = (slots[name] || (slots[name] = []));
			//如果是template元素,则把template的children添加进数组中,这也就是为什么你写的template标签并不会被渲染成另一个标签到页面
			if(child.tag === 'template'){
				slot.push.apply(slot, child.children || [])
			}else{
				slot.push(child)
			}
		}else{
			//如果没有就默认是default
			(slots.default || (slots.default = [])).push(child);
		}
	}
	//ignore slots that contains only whitespace
	for(var name$1 in slots){
		if(slots[name$1].every(isWhitespace)){
			delete slots[name$1]
		}
	}
	return slots
}

_render 渲染函数通过 normalizeScopedSlots 得到 vm.$scopedSlots

vm.$scopedSlots = normalizeScopedSlots(
	_parenVnode.data.scopedSlots,
	vm.$slots,
	vm.$scopedSlots
)

作用域插槽中父组件能够得到子组件的值是因为在 renderSlot 的时候执行会传入 props ,也就是上述 _t 第三个参数,父组件则能够得到子组件传递过来的值
参考文献1
参考文献2
参考文献3
参考文献4

### UniApp 插槽 Slot 的最佳实践 #### 基本概念与适用场景 插槽(Slot)是 Vue 提供的一种机制,允许在父组件中向子组件动态注入内容。这种特性使得组件更加灵活和可重用。在 UniApp 中,插槽的使用方式继承自 Vue,支持默认插槽、具名插槽以及作用域插槽三种形式[^1]。 为了更好地利用插槽功能,在实际项目中应遵循以下几点最佳实践: --- #### 1. **合理规划插槽数量** 过多的插槽会增加组件复杂度并降低维护性。因此建议仅定义必要的插槽数量,并确保每个插槽都有清晰的功能定位。如果发现某组件需要大量插槽,则可能表明该组件职责过广,需考虑拆分为更小粒度的子组件[^2]。 ```html <!-- 子组件 --> <template> <view class="container"> <!-- 默认插槽 --> <slot></slot> <!-- 具名插槽 --> <slot name="header"></slot> <slot name="footer"></slot> </view> </template> ``` --- #### 2. **优先使用默认插槽** 当只有一个内容区域需要被填充时,默认插槽是最简单直观的选择。它无需额外命名即可完成任务,减少不必要的复杂操作[^4]。 ```html <!-- 父组件 --> <child-component> 我是默认插槽的内容 </child-component> ``` --- #### 3. **善用具名插槽增强布局灵活性** 对于复杂的界面结构,可以通过具名插槽提供多个独立的内容入口点。这有助于提高 UI 设计自由度,使同一套逻辑能够适配不同样式需求[^3]。 ```html <!-- 子组件 --> <template> <view class="layout"> <view class="header"><slot name="header"></slot></view> <view class="main"><slot></slot></view> <view class="footer"><slot name="footer"></slot></view> </view> </template> <!-- 父组件 --> <child-component> <template v-slot:header>这是头部内容</template> <p>主体部分文字。</p> <template v-slot:footer><button>按钮</button></template> </child-component> ``` --- #### 4. **充分利用作用域插槽共享数据** 作用域插槽允许将子组件内部的数据暴露给父组件使用,从而实现高度定制化视图展示效果。这种方式特别适合构建通用型列表项或其他依赖外部渲染逻辑的基础单元[^5]。 ```html <!-- 子组件 --> <template> <view> <slot :item="dataItem"></slot> </view> </template> <script> export default { data() { return { dataItem: '来自子组件的信息' }; } }; </script> <!-- 父组件 --> <child-component v-slot:default="{ item }"> <text>{{ item }}</text> </child-component> ``` --- #### 5. **保持一致性与文档化** 无论采用哪种类型的插槽,都应当为其制定统一的行为准则,并通过注释或者单独文件记录下来以便团队成员理解如何正确调用这些 API 。良好的沟通能有效避免因误解而导致的问题发生。 --- #### 性能优化注意事项 尽管插槽极大提升了开发效率,但也可能存在性能隐患。例如频繁切换大型 DOM 节点可能导致重新计算开销增大;另外某些情况下过度嵌套也可能引发回流/重绘现象加剧等问题。针对这些问题可以从以下几个方面入手解决: - 避免无意义地重复创建相同片段; - 对于静态内容尽量提取至全局资源池管理而非实时生成; - 关键路径上的动画过渡要谨慎处理以免干扰主线程流畅运行状态。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值