vue 为我们提供了很多复用性的方式,slot 和 mixins 就是其中两种…下面对这两种方式做一下记录
开发中使用到了mixin混入与slot插槽。
mixin
vue中关于代码复用的一个实现方式,通过将多个组件的相同代码块提取出来放入到一个js文件中,再通过 mixins: [mixin]混入到组件内达到代码复用的目的,在项目具有多个相似功能的组件时很有用。
合并规则
钩子函数:同名钩子皆生效,且mixin会先于组件调用
数据对象:同名发生冲突时以组件内数据优先
值为对象的选项:methods、components、filters、directives,同数据合并方式。
( 这种规则方便于对mixin中不适用当前组件的代码进行灵活覆盖 )
插个眼:mixin在vue3中有了其替代品Composition API,其优点有
slot
创建组件时放置的一个占位,可在调用组件时自定义占位的具体显示内容,实现了复用组件的个性化的需求,在ui框架中应用广泛。
创建一个MyA.vue组件
匿名插槽
// MyA.vue:
<template>
<a v-bind:href="url" class="nav-link" >
<slot></slot>
</a>
</template>
// 使用组件:
<my-a>
这里的内容会显示在<slot></slot>中
</my-a>
具名插槽
// MyA.vue:
<template>
<a v-bind:href="url" class="nav-link" >
<slot name="slot1"></slot>
<slot name="slot2"></slot>
<slot></slot>
</a>
</template>
// 使用组件:
<my-a>
<template v-slot:slot1> 这里的内容会显示在slot1中 </template>
<template v-slot:slot2> 这里的内容会显示在slot2中 </template>
这里的内容仍然会显示在<slot></slot>中
匿名插槽有一个default名字
所以这样也行:
<template v-slot:default> 这里的内容会显示在slot中 </template>
</my-a>
作用域插槽
// MyA.vue:
// 组件使用时的作用域为父组件,不能直接使用组件内部的数据
<template>
<a v-bind:href="url" class="nav-link" >
// 需在slot上绑定数据
<slot v-bind:data="data">
{{ data.value }}
</slot>
</a>
</template>
// 使用组件:
<my-a>
<template v-slot:default="receiveData">
//这里使用
{{ receiveData.data }}
</template>
</my-a>
插槽使用场景
- 该组件被多个地方使用 - 每个父组件中对该组件的内部有一部分需要特殊定制 - slot可以让我们更好的复用组件的同时并对其定制化处理 - 可以理解为父组件想子组件传递了一段 html 文本 要求: 1.子组件模板包含至少一个 插槽 <slot>< /slot > 2.父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身 |
1.普通插槽 slot
父组件: 负责分发插槽内容 <child ref=child> 我是父组件分发给 child 的所有内容 < /child > 父组件获取子组件可以通过 this.$refs.child 来做操作 子组件: <template> <slot>这里可以放一些默认值< /slot > < /template > 模板中放置一个 <slot>< /slot >组件, 我们可以自定义组件中的方法和数据,封装一些通用逻辑,比如前几篇中封装的 scroll滚动组件 |
2.具名插槽 子组件通过 name 属性 来匹配父组件分发的内容
父组件: 添加 slot 属性来作为标识 <div slot= "header" >我是 header 分发的内容 111< /div > <div slot= "main" >我是 main 分发的内容222< /div > <div slot= "footer" >我是 footer 分发的内容333< /div > 在2.6.0 以上使用的是 v -slot:header; 默认插槽为: v -slot:default 子组件: slot 添加 name 属性来接受父组件分发的 DOM 元素 <template> <slot name= "header" >< /slot > <slot name= "main" >< /slot > <slot name= "footer" >< /slot > < /template > 当然,我们还可以调换插槽的位置... |
3.作用域插槽 父组件可以接收来自子组件的 slot 传递过来的参数值
可以理解为: 子组件中的作用域插槽可以为父组件中的插槽的展示提供数据 子组件: <template> <div> <slot name= "header" :value= "value" >< /slot > < /div > < /template > <script> export default { data() { return { value: '我是子组件的值' } } } < /script > 父组件: <child> <template slot= "header" slot-scope= "slotHeaderProps" > 渲染子组件传过来的对象中 value值{{ slotHeaderProps.value }} < /template > < /child > 在 2.6 以上绑定值的方式: v -slot:header= "slotHeaderProps" 而且可以使用解构 v -slot:header= "{value}" , 将子组件传过来的值解构 还有就是, 我们可以把 slot直接写在子组件行内, 不必另起一个 template 即这样: <child v -slot:header= "{value}" >{{value}}< /child > |
vue3.0以后 slot 和 slot=”xxx”,slot-scope 的方式会被废弃…
新的用法slot, v-slot:xxx || v-slot:default, v-slot:xxx=”slotProps”
混入 Mixins 使用
- 也是为了实现代码逻辑复用 - 当多个组件中出现业务逻辑重复时我们就可以抽离重复代码片段,写成一个混入对象 - 父组件直接引入这个对象 |
代码演示
就拿一个比较常见的场景: 下拉加载更多数据; 这类业务在H5端可以说是非常常见了,当我们很多页面都要用到时,就可以抽离成一个混入对象
// 滚动加载 import {throttle} from "@/common/js/tool" ; export const scrollMixin = { methods: { doScrollLoading() { // 滚动超出高度 let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; // 滚动区域高度 let scrollHeight = document.body.scrollHeight || document.body.scrollHeight; // 可视区高度 let clientHeight = document.documentElement.clientHeight || document.body.clientHeight; if (scrollHeight - clientHeight - scrollTop <= this.bottomHeight) { // 组件中需加入开关和加载更多; 还是有些耦合了... if (!this.isLoadMore) { this.loadMore(); } } } }, computed: { bottomHeight() { return this.$store.state.footerHeight; } }, mounted() { window.addEventListener( "scroll" , throttle(this.doScrollLoading, 100, 1)); }, destroyed() { window.removeEventListener( "scroll" , throttle(this.doScrollLoading, 100, 1)); } } |
父组件中引入使用
import { scrollMixin } from "@/mixins/scrollMixin" ; mixins: [scrollMixin] 注意组件中重写的方法会覆盖混入中的方法,loadMore和 isloadMore 需要在 引用的组件中进行重写 |