Fragment 數(shù)據(jù)懶加載及原理

Fragment 數(shù)據(jù)懶加載及原理

最近據(jù)后臺同事反饋說,某些接口調(diào)用的頻率有點高,而這塊業(yè)務(wù)還沒完全開放,照理說很少會用到,于是讓我查查怎么回事。 我看了下日志,把**請求日志過濾出來,發(fā)現(xiàn)的確有問題,每次打開首頁后都有許多那塊業(yè)務(wù)相關(guān)的**請求。

于是馬上聯(lián)想到可能是因為首頁改版之后嵌套使用了 ViewPager,業(yè)務(wù)未完全開放的那個 fragment 里嵌套了一個 ViewPager,里面有多個 fragment,這樣每次打開首頁都會去加載該 page,然后是一連串的 fragment 初始化以及**請求,所以為了解決該問題就不得不使用懶加載。

最終想要實現(xiàn)的效果是:1) 當(dāng) fragment 不可見的時候不加載數(shù)據(jù);2) 當(dāng)數(shù)據(jù)已經(jīng)加載過之后,除非手動刷新否則不重新請求數(shù)據(jù)。 首先,默認(rèn)情況下,由于 ViewPager 會預(yù)加載左右兩邊相鄰的 至少 1 個 fragment,通過 setOffscreenPageLimit() 設(shè)置預(yù)加載 page 數(shù)為 0 并不會起作用,這點從 ViewPager 的源碼中可以看到: 從以上源碼可以看出相鄰 fragment 的加載是必然的,但是我們?nèi)绻梢缘弥?fragment 可見性,那么就可以在 fragment 可見時才去加載數(shù)據(jù)。這樣雖然不是完全的懶加載,只是數(shù)據(jù)懶加載,但是同樣也可以滿足我們的需求了。 那么 fragment 中有沒有可以獲取當(dāng)前 fragment 是否可見的方法呢,當(dāng)然是有的,它就是 setUserVisibleHint(boolean isVisibleToUser) 。

無論你使用的是 FragmentPagerAdapter 還是 FragmentStatePagerAdapter,當(dāng)它們初始化 fragment 的時候,該方法都會被調(diào)用兩次。 一次是在實例化的時候,也就是在 instantioateItem() 方法中: 一次是在用戶滑動到當(dāng)前 fragment 的時候,在 setPrimaryItem() 方法中: 另外,當(dāng)用戶從當(dāng)前 fragment 滑出的時候,setPrimaryItem() 方法也會被調(diào)用。 來看下 setUserVisibleHint() 的注釋: 系統(tǒng)正是通過該方法來判斷當(dāng)前 fragment 的 UI 是否對用戶可見,而該方法被暴露出來的主要目的也是讓我們可以提醒系統(tǒng)當(dāng)前 fragment 已經(jīng)不可見了,是時候重新更新 fragment 的生命周期了。

不過如果只是實現(xiàn)數(shù)據(jù)懶加載,我們不需要直接去調(diào)用該方法,只要覆寫它并實現(xiàn)控制數(shù)據(jù)加載的邏輯就可以了。 這里我參考了一種比較簡便的做法,原文來自 尹star 的 ViewPager+Fragment LazyLoad **解 。 實現(xiàn)效果: lazy_load_fragment_demo 項目地址: aJIEw/DemoUI-LazyLoadFragment 可以看到只有**次進(jìn)入 fragment 的時候才會加載數(shù)據(jù),而且也不會主動加載相鄰的 fragment 或者已經(jīng)加載過的數(shù)據(jù)了。

首先,由于 setUserVisibleHint() 會在 fragment 實例化時就先被調(diào)用 (在 onAttach() 之前),所以我們**在 view 創(chuàng)建完畢之后加載數(shù)據(jù),因此需要設(shè)置一個 view 是否初始化完畢的標(biāo)志位。另外,當(dāng)然也需要一個 view 是否可見的標(biāo)志位,只有等到 view 可見才允許加載。然后還可以選擇保存數(shù)據(jù)的初始化狀態(tài),這樣可以控制在 fragment 生命周期中的合適時機(jī)重新加載數(shù)據(jù)。

所以,我們需要以下 3 個標(biāo)志位: 然后接下來分為兩種情況,一種是 view 初始化完畢但是此時還不可見的情況。很顯然,我們只要判斷 setUserVisibleHint() 中參數(shù)的值就可以了: 還有一種情況是,如果當(dāng)前 fragment 是整個 ViewPager 的**個 fragment,那么 setUserVisibleHint(true) 會在 view 初始化之前就在 setPrimaryItem() 中被調(diào)用,此時 view 已經(jīng)可見了,但是我們要等到 view 初始化才加載數(shù)據(jù),所以我們要在某個地方判斷 view 是否已經(jīng)初始化并且去加載數(shù)據(jù)。 **的地方是在 onActivityCreated() 中。根據(jù) fragment 生命周期我們知道,onActivityCreated() 會在 onCreateView() 之后調(diào)用,此時 view 已經(jīng)初始化完畢,我們可以在這里將 isViewInitiated 標(biāo)記為 true,同時在這里為**個顯示的 fragment 加載數(shù)據(jù): **,我們還需要判斷下數(shù)據(jù)是否已經(jīng)加載過,避免重復(fù)加載。

我們將以上所有判斷邏輯寫在 prepareFetchData() 中,判斷條件為 view 已經(jīng)初始化、可見且數(shù)據(jù)未加載: **再定義一個抽象方法 fetchData(),讓子類去實現(xiàn): 這樣一個完整的數(shù)據(jù)懶加載就實現(xiàn)完畢了。 我們可以看下以上操作的日志來驗證下我們的想法。 **次打開,F(xiàn)irstFragment 作為**個可見的 fragment 立馬被初始化: 此時 isVisibleToUser 會在 isViewInitiated 之前設(shè)為 true,所以 FirstFragment 會在 onActivityCreated() 中真正開始獲取數(shù)據(jù)。 另外,由于預(yù)加載的存在,SecondFragment 也會被創(chuàng)建,但是此時還不可見: 當(dāng)滑動到 SecondFragment 的時候,SecondFragment 狀態(tài)變?yōu)榭梢姡瑂etUserVisibleHint(true) 被調(diào)用,所以開始獲取數(shù)據(jù): 而此時 FirstFragment 由可見變?yōu)椴豢梢姡?ThirdFragment 則開始**次被創(chuàng)建,同樣此時并不可見: 當(dāng)滑動到 ThirdFragment 的時候,狀態(tài)變?yōu)榭梢?,所以也就開始獲取數(shù)據(jù): 此時 SecondFragment 由可見變?yōu)椴豢梢姡?而 FirstFragment 由于超出了 ViewPager 可以保存的 Fragment 的數(shù)量,所以被銷毀: 此時 SecondFragment 重新變得可見: 而 FirstFragment 也開始重新被創(chuàng)建: 此時 FirstFragment 重新變得可見,雖然 FirstFragment 之前被銷毀了,但是由于之前獲取的數(shù)據(jù)會被恢復(fù),所以現(xiàn)在不會重新去獲取數(shù)據(jù): 當(dāng)然我們也可以選擇在 onDestroy() 中將 isDataInitiated 置為 false,這樣每次 fragment 重新創(chuàng)建都會重新獲取數(shù)據(jù)。

當(dāng)然前提是你使用的是 FragmentStatePagerAdapter ,因為如果使用 FragmentPagerAdapter ,不會每次都調(diào)用 onDestroy(),fragment 實例會被保存。而 SecondFragment 再次變得不可見,ThirdFragment 被銷毀,過程與 3 中移動到 ThirdFragment 類似,這里就不截圖了。 通過以上日志,驗證了我們的想法是對的。 另外,如果是 ViewPager 嵌套 ViewPager 其實效果也是一樣的,如果不做特殊處理,相鄰的 fragment 的會被加載,導(dǎo)致該 fragment 中的 ViewPager 會去加載其中的 fragment。

vue多個相同組件懶加載數(shù)據(jù)

原先做法通過獲取商品組件的外層class來獲取dom判斷是否在可視窗口內(nèi),后來發(fā)現(xiàn)判斷的始終是**個掛載的商品,所以頁面一掛載,所有的json都加載出來了;后來想著vue.$ref能獲取到組件中的dom,所以能夠判斷當(dāng)前的組件中的元素是否在可視范圍內(nèi),如果在,就去加載json文件,然后判斷當(dāng)前的商品是否是下架的,如果是下架的再通過v-if刪掉當(dāng)前的百科組件dom。

el-cascader級聯(lián)選擇懶加載數(shù)據(jù)回顯解決辦法

親測有效!?。?:options=\”cascaderOptions\”,自己組裝options是回顯的關(guān)鍵?。?! 要確認(rèn)組裝的數(shù)據(jù)格式是label、和value,要么就要在props設(shè)置對應(yīng)關(guān)系!??!