原來vue的slot能夠這麼玩轉

vue的內容分發很是適合「固定部分+動態部分」的組件的場景,固定部分能夠是結構固定,也能夠是邏輯固定,好比下拉loading,下拉loading只是中間內容是動態的,而拉到底部都會觸發拉取更多內容的操做,所以咱們能夠把下拉loading作成一個有slot的插件。vue

使用場景

「下拉加載更多」的場景在移動端相對來講出現得比較多。咱們知道下拉觸底都要監聽觸底事件,觸底的操做也相同(去後臺拉取數據),分頁算法也相同,所以咱們會想到把它作成一個組件,重用這些相同的地方,讓其餘地方能夠共用這個組件,從而減小代碼量。
然而,下拉loading並非一個能夠徹底重用的組件,由於列表裏面的內容不一樣,空白頁(沒有內容時)的內容也可能不一樣,若是要作成組件,那麼就要考慮到這方面的「不一樣」,所以咱們想到利用vue的內容分發slot來作。下面是本人在開發的時候作的一個下拉loading,你們能夠參考下。算法

組件代碼bash

<template>
  <div>
    <slot name="list" v-if="total > 0"></slot>
    <slot name="empty" v-else></slot>
  </div>
</template>
<script>
import Toast from 'lib/xl-toast'

import Tool from 'tool/tool'

export default {
  data() {
    return {
      page: 1,
      isLoading: false,
      busy: false,
      isFirstLoad: false
    }
  },
  props: {
    pageSize: {
      default: 10 // 每頁展現多少條數據
    },
    total: {
      default: 0 // 總共多少條記錄
    }
  },
  computed: {
    totalPage() {
      return Math.ceil(this.total / this.pageSize)
    }
  },
  created() {
    this.getList()
  },
  mounted() {
    this.addScrollListener()
  },
  methods: {
    addScrollListener() {
      // 添加監聽滾動操做,用到函數防抖
      this.scrollFn = Tool.throttle(this.onScroll, 30, 30)
      document.addEventListener('scroll', this.scrollFn, false)
    },
    getList() {
      // 正在拉取數據或者沒有數據了,則取消滾動監聽
      if(this.isLoading || this.isFirstLoad && (this.page > this.totalPage)) {
        document.removeEventListener('scroll', this.scrollFn, false)
        return
      }
      this.busy = true
      this.isLoading = true
      // 通知父組件去拉取更多數據
      this.$emit("getList", this.page, () => {
        this.isFirstLoad = true
        this.isLoading = false
        this.page++
      }, () => {
        Toast.show('網絡錯誤,請稍後重試')
        this.total = 0
        this.isLoading = false
      })
    },
    reset() {
      // 從新拉取數據
      this.page = 1
      this.total = 0
      this.isLoading = false
      this.isFirstLoad = false
      this.addScrollListener()
      this.getList()
    },
    onScroll() {
      // 到底拉取更多數據 
      if(Tool.touchBottom()) {
        this.getList()
      }
    }
  }
}
</script>

複製代碼

總之,遇到一些有想對比較固定的部分,包括js操做或者結構固定,又有一些動態的部分,咱們應該就應該考慮到使用:組件+slot。網絡

意向不到的slot另類用法

我在作需求的時候,作了一個組件,該組件分爲上下兩個部分,這兩個部分耦合度很高(否則我怎麼把它當成一個組件呢哈哈哈),以下圖所示: 函數

原本C區域是一個組件,而後產品忽然說,須要把這兩個部分分開,把A移到C1的位置,C1移到A的位置(內心感受到憋屈)。
這裏個人第一個想法就是拆開來作成兩個組件,可是問題來了,以前這兩部分的耦合度很高,若是強制把它拆開成兩個組件,那麼這兩個組件之間的交互必然會多不少。好比,C1改變了某個東西會影響到C2,那麼C1須要觸發事件通知父組件,父組件再調用C2的某個方法來更新狀態。這種跨組件之間的通信在組件之間頻繁交互的狀況下,將會是噩夢,而我這邊卻須要頻繁的交互,因此若是把它拆分爲兩個組件,那麼工做量和複雜度將會大大的增長。固然,你能夠想到經過Event Hub的方式來實現兩個組件之間的交互,可是根本問題仍是沒有實質性得獲得解決。
那麼,有什麼方法能夠作到不拆分紅兩個組件又能移動位置的方法呢,答案就是slot。以個人例子爲例,把A和B做爲C的內容分發,原來是這樣的:

<A></A>
<B></B>
<C></C>
複製代碼

改成slot之後是這樣的ui

<C>
<A slot="c1"></A>
<B slot="c2"></B>
</C>
複製代碼

這樣就能作到不把C模塊拆分,又能調整位置了,以最小的代價完成需求~~。this

總結

vue的slot不只能夠用來內容分發,還能夠用來作位置調整。若是在須要拆分組件來作位置調整,又不想由於拆分耦合度很高的組件,能夠考慮使用slot來進行位置調整。一點愚見,但願對你們有所幫助。spa

相關文章
相關標籤/搜索