vue 的内容分发非常适合 "固定部分 + 动态部分" 的组件的场景, 固定部分可以是结构固定, 也可以是逻辑固定, 比如下拉 loading, 下拉 loading 只是中间内容是动态的, 而拉到底部都会触发拉取更多内容的操作, 因此我们可以把下拉 loading 做成一个有 slot 的插件.
使用场景
"下拉加载更多" 的场景在移动端相对来说出现得比较多. 我们知道下拉触底都要监听触底事件, 触底的操作也相同(去后台拉取数据), 分页算法也相同, 因此我们会想到把它做成一个组件, 重用这些相同的地方, 让其他地方可以共用这个组件, 从而减少代码量.
然而, 下拉 loading 并不是一个可以完全重用的组件, 因为列表里面的内容不同, 空白页 (没有内容时) 的内容也可能不同, 如果要做成组件, 那么就要考虑到这方面的 "不同", 因此我们想到利用 vue 的内容分发 slot 来做. 下面是本人在开发的时候做的一个下拉 loading, 大家可以参考下.
组件代码:
- <template>
- <div>
- <slot name="list" v-if="total> 0"></slot>
- <slot name="empty" v-else></slot>
- </div>
- </template>
- <script>
- import Toast from 'lib/xl-toast'
- import Tool from 'tool/tool'
- export default {
- data() {
- return {
- page: 1,
- isLoading: false,
- busy: false,
- isFirstLoad: false
- }
- },
- props: {
- pageSize: {
- default: 10 // 每页展示多少条数据
- },
- total: {
- default: 0 // 总共多少条记录
- }
- },
- computed: {
- totalPage() {
- return Math.ceil(this.total / this.pageSize)
- }
- },
- created() {
- this.getList()
- },
- mounted() {
- this.addScrollListener()
- },
- methods: {
- addScrollListener() {
- // 添加监听滚动操作, 用到函数防抖
- this.scrollFn = Tool.throttle(this.onScroll, 30, 30)
- document.addEventListener('scroll', this.scrollFn, false)
- },
- getList() {
- // 正在拉取数据或者没有数据了, 则取消滚动监听
- if(this.isLoading || this.isFirstLoad && (this.page> this.totalPage)) {
- document.removeEventListener('scroll', this.scrollFn, false)
- return
- }
- this.busy = true
- this.isLoading = true
- // 通知父组件去拉取更多数据
- this.$emit("getList", this.page, () => {
- this.isFirstLoad = true
- this.isLoading = false
- this.page++
- }, () => {
- Toast.show('网络错误, 请稍后重试')
- this.total = 0
- this.isLoading = false
- })
- },
- reset() {
- // 重新拉取数据
- this.page = 1
- this.total = 0
- this.isLoading = false
- this.isFirstLoad = false
- this.addScrollListener()
- this.getList()
- },
- onScroll() {
- // 到底拉取更多数据
- if(Tool.touchBottom()) {
- this.getList()
- }
- }
- }
- }
- </script>
总之, 遇到一些有想对比较固定的部分, 包括 JS 操作或者结构固定, 又有一些动态的部分, 我们应该就应该考虑到使用: 组件 + slot.
意向不到的 slot 另类用法
我在做需求的时候, 做了一个组件, 该组件分为上下两个部分, 这两个部分耦合度很高(不然我怎么把它当成一个组件呢哈哈哈), 如下图所示:
本来 C 区域是一个组件, 然后产品突然说, 需要把这两个部分分开, 把 A 移到 C1 的位置, C1 移到 A 的位置(心里感觉到憋屈).
这里我的第一个想法就是拆开来做成两个组件, 但是问题来了, 之前这两部分的耦合度很高, 如果强制把它拆开成两个组件, 那么这两个组件之间的交互必然会多很多. 比如, C1 改变了某个东西会影响到 C2, 那么 C1 需要触发事件通知父组件, 父组件再调用 C2 的某个方法来更新状态. 这种跨组件之间的通讯在组件之间频繁交互的情况下, 将会是噩梦, 而我这边却需要频繁的交互, 所以如果把它拆分为两个组件, 那么工作量和复杂度将会大大的增加. 当然, 你可以想到通过 Event Hub 的方式来实现两个组件之间的交互, 但是根本问题还是没有实质性得得到解决.
那么, 有什么方法可以做到不拆分成两个组件又能移动位置的方法呢, 答案就是 slot. 以我的例子为例, 把 A 和 B 作为 C 的内容分发, 原来是这样的:
- <A>
- </A>
- <B>
- </B>
- <C>
- </C>
改为 slot 以后是这样的
- <C>
- <A slot="c1">
- </A>
- <B slot="c2">
- </B>
- </C>
这样就能做到不把 C 模块拆分, 又能调整位置了, 以最小的代价完成需求~~.
总结
vue 的 slot 不仅可以用来内容分发, 还可以用来做位置调整. 如果在需要拆分组件来做位置调整, 又不想因为拆分耦合度很高的组件, 可以考虑使用 slot 来进行位置调整. 一点愚见, 希望对大家有所帮助.
来源: https://juejin.im/post/5ba453536fb9a05cf67a8729