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

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

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

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

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

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

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

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

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

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

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

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

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

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

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