ホームページ  >  記事  >  ウェブフロントエンド  >  vue mint-ui のloadmore コンポーネントの分析

vue mint-ui のloadmore コンポーネントの分析

小云云
小云云オリジナル
2018-01-25 10:33:032421ブラウズ

この記事では主にvue mint-uiのソースコード解析のloadmoreコンポーネントを紹介しますが、非常に優れていると思いますので、参考にさせていただきます。編集者をフォローして見てみましょう。皆さんのお役に立てれば幸いです。

アクセス

公式アクセスドキュメントmint-uiloadmoreドキュメント

使用例へのアクセス

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 コンポーネントは、name=top、name=bottom、default の 3 つのスロットで構成されます。

  • top は、プルダウン更新のさまざまな状態で表示されるコンテンツを表示するために使用されます。初期設定 Margin-top は、それ自体を非表示にするための -top の高さです

  • bottom は、top と同じで、プルアップを表示し、さまざまな状態で表示されるコンテンツをさらに読み込むために使用されます

  • デフォルトは、スクロールの詳細を入力します

実装原理

  • 主にjsのtouchイベントを監視して実装します

  • touchmoveイベントで、下にスライドしてスクロールDOMのscrollTopが0の場合、コンポーネント全体がオフセットされます下向き (スライド距離/比率) は、上部のソルトの内容を表示します

  • タッチムーブ時間中に、上にスライドして下にスライドすると、コンポーネント全体を上にスライドし続け、(スライド距離/比率) をオフセットします。一番下のソルトの内容を表示

ソースコード解析

コンポーネントテンプレート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() {
 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 分析


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 を通じて監視する 2 つの変数です。後で、それらの変更が 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);
 }

上記のコード ロジックは非常に単純で、コメントは比較的少ないです。

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 が wi​​ndow でない場合、上記の判定条件は 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() は一番下までスクロールするかどうかを決定します。これら 3 つのメソッドは参照に使用できます

  4. 欠点: 上部スロットの高さ一番下のスロットはハードコーディングされており、柔軟性が低すぎます。この場所は最適化できます

関連推奨事項: mint-ui 都市選択に基づく 3 レベルの連携実装方法

vue mint- ui模倣淘宝網京東配達先住所4レベル連携

無限スクロール読み込みを実現するVue.jsのモバイルコンポーネントライブラリmint-uiの詳細説明

以上がvue mint-ui のloadmore コンポーネントの分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。