vue minuti 分页实现

来谈下使用 vue+minuti 如何实现上拉,下拉分页数据加载操作。
分两块内容:

  • minuti loadmore 使用示例
  • 分页中可以积累的知识点

minuti loadmore 组件

官方 API

template

1
2
3
4
5
6
<mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" ref="loadmore">
<div class="line-item" v-for="(item,index) in list" :key="index">
<span class="title">{{index}}</span>
<span class="desc">{{item.id}}</span>
</div>
</mt-loadmore>

vue script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
methods: {
// 模拟接口数据
buildList: function () {
let nextPageList = [];
for (let index = 1; index <= 15; index++) {
nextPageList.push({ id: new Date().getTime() + Math.random(1) * 10 });
}
return nextPageList;
},
// 下拉
loadTop: function () {
console.log('loadTop');
this.list = this.buildList();
let self = this;
// 重置上拉开关
self.allLoaded = false;
setTimeout(function () {
// 消除loading icon
self.$refs.loadmore.onTopLoaded();
}, 500)
},
loadBottom: function () {
console.log('loadBottom');
let self = this;
// 关闭上拉加载
self.allLoaded = true;
setTimeout(function () {
// 校验接口数据,判断是否继续打开上拉加载开关
self.allLoaded = false;
self.list = self.list.concat(self.buildList());
// 消除loading icon
self.$refs.loadmore.onBottomLoaded();
}, 500)
}
}

目前比较主流的前端框架都有非常好用的基础 UI 组件库,比如:vue 国内就有 mintui、elementui。只用非常简单,对照 API 说明即可完成功能实现。

需要 get 的点

组件库的出现当然为了让开发更容易上手开发应用,提高开发效率。相反也有弊端,我们逐渐缺乏对功能实现的技巧,甚至为了完成某些效果,项目里融入了多个组件库。(所以定期看下比较好用的 npm 库,是提升自己,不被行业淘汰的方式之一)

如下给出一些可能平时忽略的技术点:

transform

transform 设置 translate3d 的 Y 轴,实现类似 native 那种拉动效果。

其他一些例子

computed & methods & watch 区别

  • 计算属性(computed)是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值
  • 而 methods 则需要主动调用。
  • 监听(watch)是在 Vue 实例化的时候调用,遍历 watch 对象的每一个属性。

在 loadmore 组件中,最频繁的就是上拉下载操作,所以使用了 computed

1
2
3
4
5
computed: {
transform() {
return this.translate === 0 ? null : 'translate3d(0, ' + this.translate + 'px, 0)';
}
}

对于一些延迟返回的 status 判断,使用了 watch 来监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
topStatus(val) {
this.$emit('top-status-change', val);
switch (val) {
case 'pull':
this.topText = this.topPullText;
break;
case 'drop':
this.topText = this.topDropText;
break;
case 'loading':
this.topText = this.topLoadingText;
break;
}
}

\$ref

ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 \$refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

1
self.$refs.loadmore.onTopLoaded();
1
2
3
4
5
6
onTopLoaded() {
this.translate = 0;
setTimeout(() => {
this.topStatus = 'pull';
}, 200);
}

但是要注意:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!\$refs 也不是响应式的

\$nextTick

将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。

用于 Dom 渲染完,某些数据的更新,则需要使用 nextTick 加入 callback,重新 push 到 vue 渲染机制中。

当然没有那么简单,更多网站有大堆的针对 nextTick 的解读

1
2
3
4
5
6
onBottomLoaded() {
this.$nextTick(() => {
// do something
this.translate = 0;
});
}

nodeType

用来区分 Html 里 dom 节点类型

常量 value 说明
Node.ELEMENT_NODE 1 元素节点
Node.TEXT_NODE 3 元素节点或者文字
Node.PROCESSING_INSTRUCTION_NODE 7 xml 顶部的声明
Node.COMMENT_NODE 8 注释
Node.DOCUMENT_NODE 9 document.nodeType
Node.DOCUMENT_TYPE_NODE 10 document.doctype.nodeType
Node.DOCUMENT_FRAGMENT_NODE 11 虚拟节点 document.createDocumentFragment().nodeType
1
2
3
4
5
6
7
8
9
10
11
12
13
getScrollEventTarget(element) {
let currentNode = element;
// 判断是否全局滚动条
while (currentNode && currentNode.tagName !== 'HTML' &&
currentNode.tagName !== 'BODY' && currentNode.nodeType === 1) {
let overflowY = document.defaultView.getComputedStyle(currentNode).overflowY;
if (overflowY === 'scroll' || overflowY === 'auto') {
return currentNode;
}
currentNode = currentNode.parentNode;
}
return window;
}

关于滚动

判断页面是否滚动到底部

1
2
3
4
5
if (this.scrollEventTarget === window) {
return document.body.scrollTop || document.documentElement.scrollTop + document.documentElement.clientHeight >= document.body.scrollHeight;
} else {
return this.$el.getBoundingClientRect().bottom <= this.scrollEventTarget.getBoundingClientRect().bottom + 1;
}

判断页面是否超过可视屏幕

1
2
3
4
5
if (this.scrollEventTarget === window) {
this.containerFilled = this.$el.getBoundingClientRect().bottom >= document.documentElement.getBoundingClientRect().bottom;
} else {
this.containerFilled = this.$el.getBoundingClientRect().bottom >= this.scrollEventTarget.getBoundingClientRect().bottom;
}

获取滚动条距离顶部距离

1
2
3
4
5
if (element === window) {
return Math.max(window.pageYOffset || 0, document.documentElement.scrollTop);
} else {
return element.scrollTop;
}

浏览器兼容问题

  • chrome 只认识:document.body.scrollTop
  • ie678:document.documentElement.scrollTop
  • ie9 及以上:window.pageYOffset 或者 document.documentElement.scrollTop

所以自己实现时,注意这些点,饿了吗这个框架貌似很久没有 merge 了

【长按关注】看看↓↓↓?
Eminoda wechat
【前端雨爸】分享前端技术实践,持续输出前端技术文章
欢迎留言,评论交流,一起讨论前端问题
📢 因为是开源博客,为避免 Gitalk授权 带来的 安全风险,也可访问