>웹 프론트엔드 >JS 튜토리얼 >vue mint-ui의 loadmore 구성요소 분석

vue mint-ui의 loadmore 구성요소 분석

小云云
小云云원래의
2018-01-25 10:33:032460검색

이 글은 vue mint-ui 소스 코드 분석의 loadmore 구성 요소를 주로 소개합니다. 편집자는 꽤 좋다고 생각합니다. 이제 공유하고 참고하겠습니다. 편집자를 따라 살펴보겠습니다. 모두에게 도움이 되기를 바랍니다.

Access

공식 접속 문서 mint-ui loadmore 문서

사용 접속 예시

html


<p id="app">
  <mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" :max-distance="150"
         @top-status-change="handleTopChange" ref="loadmore">

    <p slot="top" class="mint-loadmore-top">
      <span v-show="topStatus === &#39;pull&#39;" :class="{ &#39;rotate&#39;: topStatus === &#39;drop&#39; }">↓</span>
      <span v-show="topStatus === &#39;loading&#39;">Loading...</span>
      <span v-show="topStatus === &#39;drop&#39;">释放更新</span>
    </p>

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

  </mt-loadmore>
</p>

css


<link rel="stylesheet" href="https://unpkg.com/mint-ui/lib/style.css" rel="external nofollow" >
*{
  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


<!-- 先引入 Vue -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/mint-ui/lib/index.js"></script>
<script>
  new Vue({
    el: &#39;#app&#39;,
    data: {
      list: [],
      allLoaded: false,
      topStatus: &#39;&#39;
    },
    created: function () {
      var i =0, len=20;
      for (; i< len; i++){
        this.list.push(&#39;demo&#39; + i);
      }

    },
    methods: {
      loadTop: function () { // 刷新数据的操作
        var self = this;
        setTimeout(function () {
          self.list.splice(0, self.list.length);
          var i =0, len=20;
          for (; i< len; i++){
            self.list.push(&#39;demo&#39; + i);
          }
          self.$refs.loadmore.onTopLoaded();
        }, 2000);
      },
      loadBottom: function () { // 加载更多数据的操作
        //load data

        //this.allLoaded = true;// 若数据已全部获取完毕
        var self = this;
        setTimeout(function () {
          var i =0; len = 10;
          for (; i< len; i++){
            self.list.push(&#39;dddd&#39; + i);
          }
          self.$refs.loadmore.onBottomLoaded();
        }, 2000);

      },
      handleTopChange: function (status) {
        this.topStatus = status;
      },
      itemClick: function (data) {
        console.log(&#39;item click, msg : &#39; + data);
      }
    }
  });
</script>

실현된 원리 분석

레이아웃 원칙

  • loadmore 구성 요소는 이름=상단, 이름=하단, 기본값의 3개 슬롯으로 구성됩니다.

  • top은 풀다운 새로 고침의 다양한 상태로 표시되는 콘텐츠를 표시하는 데 사용됩니다. 초기 설정 Margin-top은 자신을 숨기기 위한 -top의 높이입니다.

  • bottom은 top과 동일하며 풀업을 표시하고 다른 상태에 표시된 더 많은 콘텐츠를 로드하는 데 사용됩니다.

  • 스크롤 세부 정보를 채우는 기본값

구현 원리

  • 주로 js의 터치 이벤트를 모니터링하여 구현합니다.

  • touchmove 이벤트에서 아래로 슬라이드하고 스크롤링 돔의 scrollTop이 0이면 전체 구성 요소가 오프셋됩니다. 하향(슬라이딩 거리/비율)은 상단 솔트의 내용을 표시합니다.

  • 터치무브 시간 동안 위로 슬라이드하여 하단으로 슬라이드한 다음 전체 구성 요소를 계속 위쪽으로 슬라이드하고 오프셋(슬라이딩 거리/비율)을 하단 solt 내용 표시

소스코드 분석

컴포넌트 템플릿 html


 <p class="mint-loadmore">
  <p class="mint-loadmore-content" :class="{ &#39;is-dropped&#39;: topDropped || bottomDropped}" :style="{ &#39;transform&#39;: &#39;translate3d(0, &#39; + translate + &#39;px, 0)&#39; }">
   <slot name="top">
    <p class="mint-loadmore-top" v-if="topMethod">
     <spinner v-if="topStatus === &#39;loading&#39;" class="mint-loadmore-spinner" :size="20" type="fading-circle"></spinner>
     <span class="mint-loadmore-text">{{ topText }}</span>
    </p>
   </slot>
   <slot></slot>
   <slot name="bottom">
    <p class="mint-loadmore-bottom" v-if="bottomMethod">
     <spinner v-if="bottomStatus === &#39;loading&#39;" class="mint-loadmore-spinner" :size="20" type="fading-circle"></spinner>
     <span class="mint-loadmore-text">{{ bottomText }}</span>
    </p>
   </slot>
  </p>
 </p>

위 스피너 태그에 대해서는 컴포넌트이므로 여기서는 자세히 소개하지 않겠습니다. 상단 솔트 및 하단 슬롯의 컨텐츠는 표시되는 컨텐츠이며 외부 사용자 정의를 통해 전달할 수 있습니다.

실제로 구현에는 매우 심각한 단점이 있습니다. 즉, 상단 솔더 및 하단 슬롯의 높이가 50px로 프로그래밍되어 있고 js에서의 처리도 논리적으로 50px를 사용하여 처리된다는 것입니다. 따라서 이는 개발 과정에서 상단 슬롯과 하단 슬롯을 사용자 정의해야 하는 요구 사항을 충족합니다.

js 핵심 분석

  • props 분석: props 분석은 mint-ui 공식 문서를 참고하세요

  • data analyze


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

각 데이터 데이터의 구체적인 역할 위의 내용이 전달되었습니다. 댓글에 세부정보가 제공됩니다.

watch analyze


watch: {
 topStatus(val) {
  this.$emit(&#39;top-status-change&#39;, val);
  switch (val) {
   case &#39;pull&#39;:
    this.topText = this.topPullText;
    break;
   case &#39;drop&#39;:
    this.topText = this.topDropText;
    break;
   case &#39;loading&#39;:
    this.topText = this.topLoadingText;
    break;
  }
 },

 bottomStatus(val) {
  this.$emit(&#39;bottom-status-change&#39;, val);
  switch (val) {
   case &#39;pull&#39;:
    this.bottomText = this.bottomPullText;
    break;
   case &#39;drop&#39;:
    this.bottomText = this.bottomDropText;
    break;
   case &#39;loading&#39;:
    this.bottomText = this.bottomLoadingText;
    break;
  }
 }
}

위는 컴포넌트가 watch를 통해 모니터링하는 두 가지 변수입니다. 나중에 해당 변경 사항이 touchmove 이벤트에서 처리되는 것을 볼 수 있습니다. 그 기능은 변경을 통해 상단 슬롯과 하단 슬롯의 텍스트 내용을 변경하는 것입니다.

동시에 상단 슬롯과 하단 슬롯의 내용이 변경될 수 있으므로 상태 변경 이벤트가 외부 사용을 위해 발행됩니다. 외부적으로 커스터마이징하여 이 이벤트를 통해 공지 외부 처리를 위한 외부 현재 상태입니다.

핵심 기능 분석

여기서 모든 메소드를 나열하지는 않겠습니다. 다음은 처리를 기반으로 해당 메소드 기능을 분석합니다.

우선, 진입점은 컴포넌트의 마운트된 라이프사이클의 후크 콜백에서 init 함수를 실행하는 것입니다.


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

init 함수:


init() {
  this.topStatus = &#39;pull&#39;;
  this.bottomStatus = &#39;pull&#39;;
  this.topText = this.topPullText;
  this.scrollEventTarget = this.getScrollEventTarget(this.$el); // 获取滚动的dom节点
  if (typeof this.bottomMethod === &#39;function&#39;) {
   this.fillContainer(); // 判断当前滚动内容是否填满,没有执行外部传入的loadmore回调函数加载数据
   this.bindTouchEvents(); // 为当前组件dom注册touch事件
  }
  if (typeof this.topMethod === &#39;function&#39;) {
   this.bindTouchEvents();
  }
 },

 fillContainer() {
  if (this.autoFill) {
   this.$nextTick(() => {
    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 = &#39;loading&#39;;
     this.bottomMethod();// 调用外部的loadmore函数,加载更多数据
    }
   });
  }
 }

init 함수는 주로 상태의 일부 작업을 초기화합니다. 다음은 이벤트 콜백 함수의 터치 처리 분석에 중점을 둡니다.

먼저 터치스타트 이벤트 콜백 처리 기능


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

은 주로 초기 위치 기록과 상태 변수 재설정에 사용됩니다.

계속해서 touchmove 콜백 처리 기능을 살펴보겠습니다.


 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 ? &#39;down&#39; : &#39;up&#39;;
  // 下拉刷新,条件(1.外部传入了刷新的回调函数 2.滑动方向是向下的 3.当前滚动节点的scrollTop为0 4.当前topStatus不是loading)
  if (typeof this.topMethod === &#39;function&#39; && this.direction === &#39;down&#39; &&
   this.getScrollTop(this.scrollEventTarget) === 0 && this.topStatus !== &#39;loading&#39;) {
   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 ? &#39;drop&#39; : &#39;pull&#39;;// drop: 到达指定的阈值,可以执行刷新操作了
  }

  // 上拉操作, 判断当前scroll dom是否滚动到了底部
  if (this.direction === &#39;up&#39;) {
   this.bottomReached = this.bottomReached || this.checkBottomReached();
  }
  if (typeof this.bottomMethod === &#39;function&#39; && this.direction === &#39;up&#39; &&
   this.bottomReached && this.bottomStatus !== &#39;loading&#39; && !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 ? &#39;drop&#39; : &#39;pull&#39;;
  }
  this.$emit(&#39;translate-change&#39;, this.translate);
 }

위의 코드 로직은 매우 간단하고 댓글이 상대적으로 적습니다.

현재 스크롤 DOM이 아래쪽으로 스크롤되었는지 확인하는 데 사용되는 checkBottomReached() 함수에 중점을 두겠습니다.


 checkBottomReached() {
  if (this.scrollEventTarget === window) {
   return document.body.scrollTop + document.documentElement.clientHeight >= 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 이벤트 콜백의 핸들러 함수를 살펴보겠습니다


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

요약

  1. 풀다운 새로 고침 및 풀업 로딩에 대한 더 많은 구현 원리는

  2. 에서 배울 수 있습니다.
  3. getScrollEventTarget()은 스크롤 개체를 가져오고, getScrollTop()은 스크롤 거리를 가져오고, checkBottomReached()는 아래쪽으로 스크롤할지 여부를 결정합니다. 이 세 가지 메서드는 참조용으로 사용할 수 있습니다.

  4. 단점: 상단 슬롯의 높이 그리고 하단 슬롯은 너무 유연하지 않게 하드코딩되어 있어서 최적화가 가능합니다

관련 추천 :

vue 민트의 도시 선택 3단계 연계 구현 방식

vue mint- ui 모방 Taobao Jingdong 배송 주소 4단계 연결

무한 스크롤 로딩을 달성하기 위한 Vue.js의 모바일 구성 요소 라이브러리 mint-ui에 대한 자세한 설명

위 내용은 vue mint-ui의 loadmore 구성요소 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.