详解vue mint-ui源码解析之loadmore组件

本文介绍了vue mint-ui源码解析之loadmore组件,分享给大家,具体如下:

接入

官方接入文档mint-ui loadmore文档

接入使用Example

html

rush:xhtml;">
ottom-method="loadBottom" :bottom-all-loaded="allLoaded" :max-distance="150" @top-status-change="handletopChange" ref="loadmore">
<div slot="top" class="mint-loadmore-top"&gt;
  <span v-show="topStatus === 'pull'" :class="{ 'rotate': topStatus === 'drop' }"&gt;↓</span>
  <span v-show="topStatus === 'loading'"&gt;Loading...</span>
  <span v-show="topStatus === 'drop'"&gt;释放更新</span>
</div>

<ul class="scroll-wrapper"&gt;
  <li v-for="item in list" @click="itemClick(item)"&gt;{{ item }}</li>
</ul>

css

rush:css;"> *{ margin: 0; padding: 0; } html,body{ height: 100%; }

app{

height: 100%;
overflow: scroll;
}
.scroll-wrapper{
margin: 0;
padding: 0;
list-style: none;

}
.scroll-wrapper li{
line-height: 120px;
font-size: 60px;
text-align: center;
}

js

rush:js;">

实现原理解析

布局原理

  • loadmore组件内部由三个slot组成,分别为name=top,name=bottom,default;
  • top用于展示下拉刷新不同状态展示的内容,初始设置margin-top为-top的高度来将自己隐藏
  • bottom同top,用于展示上拉加载更多不同状态展示的内容
  • default填充滚动详细内容

实现原理

  • 主要是通过js的touch事件的监听来实现
  • 在touchmove事件,如果是向下滑动并且滚动的dom的scrollTop为0,那么整个组件向下偏移(滑动的距离/比值)展示出top solt的内容
  • 在touchmove时间,如果是向上滑动并且滑动到了底部,再继续滑动整个组件向上偏移(滑动的距离/比值)展示出bottom solt的内容

源码解析

组件的template html

rush:xhtml;">
ottomDropped}" :style="{ 'transform': 'translate3d(0,' + translate + 'px,0)' }">
ottom">
ottom" v-if="bottomMethod"> ottomStatus === 'loading'" class="mint-loadmore-spinner" :size="20" type="fading-circle"> ottomText }}

关于 上面的spinner标签,是一个组件,这里不做详细介绍。top solt和bottom slot中的内容是展示的内容,可以通过外部自定义的方式传入。

其实它的实现有一个很严重的弊端,就是写死了top solt和bottom slot的高度为50px,而且js中的处理也是使用50px进行的逻辑处理。所以并满足我们开发中自定义top slot 和bottom slot的需求。

js核心解析

  • props解析:关于props的解析,可以参考mint-ui的官方文档
  • data解析
rush:js;"> data() { return { translate: 0,// 此变量决定当前组件上下移动,scrollEventTarget: null,// 滚动的dom节点 containerFilled: false,// 当前滚动的内容是否填充完整,不完成会调用 loadmore的回调函数 topText: '',// 下拉刷新,显示的文本 topDropped: false,// 记录当前drop状态,用给组件dom添加is-dropped class(添加回到原点的动画) bottomText: '',// 上拉加载更多 显示的文本 bottomDropped: false,// 同topDropped bottomreached: false,// 当前滚动是否滚动到了底部 direction: '',// touch-move过程中,当前滑动的方向 startY: 0,// touch-start 起始的y的坐标值 startScrollTop: 0,// touch-start 起始的滚动dom的 scrollTop currentY: 0,// touch-move 过程中的 y的坐标 topStatus: '',// 下拉刷新的状态: pull(下拉) drop(释放) loading(正在加载数据) bottomStatus: '' // 上拉加载更多的状态: 状态同上 }; }

上面的关于每个data数据的具体作用通过注释做了详细说明。

watch解析

rush:js;"> watch: { 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; } },bottomStatus(val) { this.$emit('bottom-status-change',val); switch (val) { case 'pull': this.bottomText = this.bottompullText; break; case 'drop': this.bottomText = this.bottomDropText; break; case 'loading': this.bottomText = this.bottomloadingText; break; } } }

上面是组件通过watch监听的两个变量,后面我们能看到他们的改变是在touchmove事件中进行处理改变的。它的作用是通过它的变化来改变top slot和bottom slot的文本内容

同时发出status-change事件给外部使用,因为可能外部自定义top slot 和bottom slot的内容,通过此事件来通知外部当前状态以便外部进行处理。

核心函数的解析

这里就不将所有的method列出,下面就根据处理的所以来解析对应的method函数

首先,入口是在组件mounted生命周期的钩子回调中执行init函数

rush:js;"> mounted() { this.init();// 当前 vue component挂载完成之后,执行init()函数 }

init函数

{ 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; } if (!this.containerFilled) { // 如果没有填满内容,执行loadmore的操作 this.bottomStatus = 'loading'; this.bottomMethod();// 调用外部的loadmore函数,加载更多数据 } }); } }

init函数主要是初始化状态和事件的一些操作,下面着重分析touch事件的回调函数的处理。

首先touchstart事件回调处理函数

rush:js;"> handletouchStart(event) { this.startY = event.touches[0].clientY; // 手指按下的位置,用于下面move事件计算手指移动的距离 this.startScrollTop = this.getScrollTop(this.scrollEventTarget); // 起始scroll dom的 scrollTop(滚动的距离) //下面重置状态变量 this.bottomreached = false; if (this.topStatus !== 'loading') { this.topStatus = 'pull'; this.topDropped = false; } if (this.bottomStatus !== 'loading') { this.bottomStatus = 'pull'; this.bottomDropped = false; } }

主要是记录初始位置和重置状态变量。

下面继续touchmove的回调处理函数

rush:js;"> handletouchMove(event) { //确保当前touch节点的y的位置,在当前loadmore组件的内部 if (this.startY < this.$el.getBoundingClientRect().top && this.startY > this.$el.getBoundingClientRect().bottom) { return; } this.currentY = event.touches[0].clientY; let distance = (this.currentY - this.startY) / this.distanceIndex; this.direction = distance > 0 ? 'down' : 'up'; // 下拉刷新,条件(1.外部传入了刷新的回调函数 2.滑动方向是向下的 3.当前滚动节点的scrollTop为0 4.当前topStatus不是loading) if (typeof this.topMethod === 'function' && this.direction === 'down' && this.getScrollTop(this.scrollEventTarget) === 0 && this.topStatus !== 'loading') { event.preventDefault(); event.stopPropagation(); //计算translate(将要平移的距离),如果当前移动的距离大于设置的最大距离,那么此次这次移动就不起作用了 if (this.maxdistance > 0) { this.translate = distance <= this.maxdistance ? distance - this.startScrollTop : this.translate; } else { this.translate = distance - this.startScrollTop; } if (this.translate < 0) { this.translate = 0; } this.topStatus = this.translate >= this.topdistance ? 'drop' : 'pull';// drop: 到达指定的阈值,可以执行刷新操作了 }

// 上拉操作,判断当前scroll dom是否滚动到了底部
if (this.direction === 'up') {
this.bottomreached = this.bottomreached || this.checkBottomreached();
}
if (typeof this.bottomMethod === 'function' && this.direction === 'up' &&
this.bottomreached && this.bottomStatus !== 'loading' && !this.bottomAllLoaded) {
event.preventDefault();
event.stopPropagation();
// 判断的逻辑思路同上
if (this.maxdistance > 0) {
this.translate = Math.abs(distance) <= this.maxDistance
? this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance : this.translate;
} else {
this.translate = this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance;
}
if (this.translate > 0) {
this.translate = 0;
}
this.bottomStatus = -this.translate >= this.bottomdistance ? 'drop' : 'pull';
}
this.$emit('translate-change',this.translate);
}

上面的代码逻辑挺简单,注释也就相对不多。

重点谈一下checkBottomreached()函数,用来判断当前scroll dom是否滚动到了底部

= document.body.scrollHeight; } else { return this.$el.getBoundingClientRect().bottom <= this.scrollEventTarget.getBoundingClientRect().bottom + 1; } }

经过我的测试,上面的代码是有问题:

当scrollEventTarget是window的条件下,上面的判断是不对的。因为document.body.scrollTop总是比正常值小1,所以用于无法满足到达底部的条件;

当scrollEventTarget不是window的条件下,上面的判断条件也不需要在this.scrollEventTarget.getBoundingClientRect().bottom后面加1,但是加1也不会有太大视觉上的影响。

最后来看下moveend事件回调的处理函数

0) { this.topDropped = true; // 为当前组件添加 is-dropped class(也就是添加动画处理) if (this.topStatus === 'drop') { // 到达了loading的状态 this.translate = '50'; // top slot的高度 this.topStatus = 'loading'; this.topMethod(); // 执行回调函数 } else { // 没有到达,回调原点 this.translate = '0'; this.topStatus = 'pull'; } } // 处理逻辑同上 if (this.direction === 'up' && this.bottomreached && this.translate < 0) { this.bottomDropped = true; this.bottomreached = false; if (this.bottomStatus === 'drop') { this.translate = '-50'; this.bottomStatus = 'loading'; this.bottomMethod(); } else { this.translate = '0'; this.bottomStatus = 'pull'; } } this.$emit('translate-change',this.translate); this.direction = ''; } }

总结

  1. 下拉刷新和上拉加载更多的实现原理可以借鉴
  2. getScrollEventTarget()获取滚动对象,getScrollTop()获取滚动距离,checkBottomreached()判断是否滚动到底部;这三种方式可以借鉴
  3. 缺点: 写死了top slot 和 bottom slot的高度,太不灵活;这个地方可以优化

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

相关文章

可以通过min-width属性来设置el-table-column的最小宽度。以...
yarn dev,当文件变动后,会自动重启。 yanr start不会自动重...
ref 用于创建一个对值的响应式引用。这个值可以是原始值(如...
通过修改 getWK005 函数来实现这一点。这里的 query 参数就是...
&lt;el-form-item label=&quot;入库类型&quot; ...
API 变动 样式类名变化: 一些组件的样式类名有所变动,可能需...