Heim  >  Artikel  >  Web-Frontend  >  Analyse der Loadmore-Komponente in Vue Mint-UI

Analyse der Loadmore-Komponente in Vue Mint-UI

小云云
小云云Original
2018-01-25 10:33:032398Durchsuche

In diesem Artikel wird hauptsächlich die Loadmore-Komponente der Vue-Mint-UI-Quellcode-Analyse vorgestellt. Jetzt werde ich sie mit Ihnen teilen und Ihnen eine Referenz geben. Folgen wir dem Herausgeber und schauen wir uns das an. Ich hoffe, es kann allen helfen.

Zugriff

Offizielles Zugriffsdokument Mint-UI Loadmore-Dokument

Zugriff anhand eines Beispiels

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>

Analyse des Implementierungsprinzips

Layoutprinzip

  • Die Loadmore-Komponente besteht intern aus drei Slots mit dem Namen = oben, Name = unten, Standard;

  • top wird verwendet, um den in verschiedenen Stadien der Pulldown-Aktualisierung angezeigten Inhalt anzuzeigen. Der Rand oben wird zunächst auf die Höhe von -top eingestellt, um sich selbst auszublenden

  • Unten ist dasselbe wie oben und wird zum Anzeigen des Pull-up-Ladens verwendet, um mehr Inhalte in verschiedenen Zuständen anzuzeigen

  • Standardmäßig werden Scrolldetails ausgefüllt

Implementierungsprinzip

  • Es wird hauptsächlich durch die Überwachung des Berührungsereignisses von js implementiert

  • Wenn es im Touchmove-Ereignis nach unten gleitet und der scrollTop-Wert des Scroll-Doms 0 ist, wird die gesamte Komponente nach unten versetzt (Gleitweg/Verhältnis), um den Inhalt des oberen Solts anzuzeigen

  • Wenn Sie zum Touchmove-Zeitpunkt nach oben und nach unten gleiten, schieben Sie dann die gesamte Komponente weiter nach oben, um einen Versatz (Gleitweg/Verhältnis) zu erreichen, um den Inhalt des unteren Verkaufs anzuzeigen

Quellcode-Analyse

Vorlage-HTML der Komponente


 <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>

Bezüglich des Spinners Tag oben, es ist eine Komponente und wird hier nicht im Detail vorgestellt. Der Inhalt im oberen und unteren Bereich ist der angezeigte Inhalt und kann durch externe Anpassung übergeben werden.

Tatsächlich weist seine Implementierung einen sehr gravierenden Nachteil auf, nämlich dass die Höhe des oberen Lots und des unteren Schlitzes auf 50 Pixel programmiert ist und die Verarbeitung in js auch 50 Pixel für die logische Verarbeitung verwendet. Daher erfüllt es die Anforderungen der individuellen Anpassung des oberen und unteren Schlitzes in unserer Entwicklung.

JS-Kernanalyse

  • Requisitenanalyse: Informationen zur Analyse von Requisiten finden Sie in der offiziellen Dokumentation von mint-ui

  • Datenanalyse


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; // 上拉加载更多的状态: 状态同上
 };
}

Die oben genannten spezifischen Funktionen der einzelnen Daten werden durch Kommentare ausführlich erläutert.

Überwachungsanalyse


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;
  }
 }
}

Die oben genannten sind die beiden Variablen, die die Komponente durch Überwachung überwacht. Später können wir sehen, dass sich ihre Änderungen im Touchmove befinden Ereignisverarbeitungsänderungen werden vorgenommen. Seine Funktion besteht darin, den Textinhalt des oberen und unteren Slots durch seine Änderungen zu ändern.

Gleichzeitig wird das Statusänderungsereignis zur externen Verwendung ausgegeben, da der Inhalt des oberen Slots geändert wird Steckplatz und unterer Steckplatz können extern angepasst werden. Verwenden Sie dieses Ereignis, um den aktuellen Status für die externe Verarbeitung nach außen zu benachrichtigen.

Analyse der Kernfunktionen

Ich werde hier nicht alle Methoden auflisten die Verarbeitungsmethodenfunktion.

Der Einstiegspunkt besteht zunächst darin, die Init-Funktion im Hook-Callback des bereitgestellten Lebenszyklus der Komponente


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

Init-Funktion auszuführen :


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函数,加载更多数据
    }
   });
  }
 }

Die Init-Funktion führt hauptsächlich einige Vorgänge zum Initialisierungsstatus und zu Ereignissen aus. Im Folgenden liegt der Schwerpunkt auf der Verarbeitung der Rückruffunktion des Berührungsereignisses.

Erstens wird die Touchstart-Ereignis-Callback-Handler-Funktion


 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;
  }
 }

hauptsächlich zum Aufzeichnen der Anfangsposition und zum Zurücksetzen von Statusvariablen verwendet.

Im Folgenden wird mit der Touchmove-Rückrufverarbeitungsfunktion fortgefahren


 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);
 }

Die obige Codelogik ist recht einfach und es gibt relativ wenige Kommentare .

Konzentrieren Sie sich auf die Funktion checkBottomReached(), mit der ermittelt wird, ob der aktuelle Scroll-Dom nach unten gescrollt wurde.


 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;
  }
 }

Nach meinen Tests gibt es ein Problem mit dem obigen Code:

Wenn scrollEventTarget ein Fenster ist, wird das obige Urteil gefällt nicht richtig. Da document.body.scrollTop immer um 1 kleiner als der Normalwert ist, kann es die Bedingung, den unteren Rand zu erreichen, nicht erfüllen.

Wenn scrollEventTarget kein Fenster ist, muss die obige Beurteilungsbedingung nicht erfüllt sein Dies Fügen Sie 1 nach .scrollEventTarget.getBoundingClientRect().bottom hinzu, aber das Hinzufügen von 1 hat keine große visuelle Wirkung.

Schauen wir uns zum Schluss die Handlerfunktion des Moveend-Ereignisrückrufs an


 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;;
 }
}

Zusammenfassung

  1. Weitere Implementierungsprinzipien der Pulldown-Aktualisierung und des Pullup-Ladens können von

  2. getScrollEventTarget() zum Abrufen des Bildlaufobjekts getScrollTop() gelernt werden Ermitteln Sie den Bildlaufabstand und prüfen Sie mit checkBottomReached(), ob diese drei Methoden als Referenz verwendet werden können.

  3. Nachteile: Die Höhe des oberen und unteren Schlitzes ist schwierig -codiert, was zu unflexibel ist. Dieser Ort kann optimiert werden auf Mint-UI

vue Mint-UI Nachahmung Taobao und JD.com Lieferadresse vierstufige Verknüpfung

Detaillierte Erläuterung der mobilen Komponentenbibliothek mint-ui von Vue.js, um unendliches Scrollen zu ermöglichen und mehr zu laden

Das obige ist der detaillierte Inhalt vonAnalyse der Loadmore-Komponente in Vue Mint-UI. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn