Virtual List 學(xué)習(xí)筆記

看了 Furybean 的文章 《再談前端虛擬列表的實(shí)現(xiàn)》 學(xué)習(xí)了下 Virtual List。記了些筆記逗堵,并且動(dòng)手實(shí)踐了一下秉氧,在此記錄一下~

大數(shù)據(jù)列表優(yōu)化方案

1. 列表中有N個(gè)高度相同的元素

  • 根據(jù)單個(gè)元素高度及元素?cái)?shù)量計(jì)算整個(gè)列表高度,使用空白元素?fù)纹鹆斜砀叨取?/li>
  • 根據(jù) ScrollTop 屬性和元素高度計(jì)算從哪個(gè)元素開(kāi)始顯示蜒秤,并通過(guò)顯示容器的高度確定最后一個(gè)需要顯示的元素汁咏,最后返回需要顯示元素的數(shù)組。
  • 用vue渲染需要顯示的列表作媚,通過(guò)scrollTop確定top元素的位置攘滩。

2. 高度不同

  • 添加單個(gè)列表元素高度計(jì)算方法 itemSizeGetter,該方法返回單個(gè)列表元素的高度掂骏。
  • 使用 itemSizeGetter 方法求和計(jì)算整個(gè)列表高度轰驳。
  • 使用 ScrollTop 屬性和 itemSizeGetter 方法計(jì)算第一個(gè)顯示的列表元素的索引和偏移量。再配合顯示容器的高度計(jì)算最后一個(gè)顯示的列表元素的索引和偏移量。最后返回需要顯示元素的數(shù)組级解。
  • 用vue渲染需要顯示的列表冒黑,通過(guò)scrollTop確定top元素的位置。

3. 使用緩存

為了避免每次滾動(dòng)列表都要反復(fù)計(jì)算勤哗,所以使用緩存將計(jì)算過(guò)的元素屬性都緩存下來(lái)抡爹。
具體做法:將所有測(cè)量計(jì)算過(guò)得元素的高度和偏移量保存在一個(gè)對(duì)象中,優(yōu)先從對(duì)象中獲取芒划,如果沒(méi)有再去計(jì)算高度和偏移量冬竟。

4. 獲取列表高度優(yōu)化

其實(shí)計(jì)算整個(gè)列表高度需要遍歷所有元素計(jì)算高度并求和,也是很消耗性能的民逼。
解決方案:定義一個(gè)所有元素的高度預(yù)估值泵殴,即所未顯示元素的平均高度。計(jì)算時(shí)根據(jù)緩存來(lái)判斷兩種情況拼苍,如果沒(méi)有緩存直接使用 數(shù)量 * 預(yù)估高度笑诅;如果有緩存,計(jì)算方法就是 緩存的偏移量 + 剩余元素?cái)?shù)量 * 預(yù)估高度疮鲫。

5. 優(yōu)化已緩存搜索

緩存數(shù)據(jù)檢索也需要消耗資源吆你,如何將資源消耗最小化?
文章中使用的是二分法搜索~(又 GET 到一個(gè)新技能 :D )這里自己試著寫個(gè)二分法試試:

    <div>
        <input id="input" type="text"/>
        <button onclick="middleSearch()">search</button>
        <span id="result"></span>
    </div>
    <script>
        let arr = []
        let i = 10000
        while(i--) {
            arr.push(i*2)
        }

        function middleSearch(){
            const value = parseInt(document.getElementById("input").value)
            if (value != 0 && !value) {
                return -1
            }
            let start = 0
            let end = arr.length - 1
            let middle
            while(start <= end) {
                middle = Math.floor((start + end)/2)
                if (value == arr[middle]) {
                    console.log(middle)
                    document.getElementById("result").textContent = middle
                    return middle
                } else if (arr[middle] > value) {
                    end = middle - 1
                } else {
                    start = middle + 1                    
                }
            }
            if (!middle) {
                middle = -1
            }
            console.log(middle)
            document.getElementById("result").textContent = middle
            return middle
        }
    </script>

注意俊犯,二分法的前提必須是有序的數(shù)組集合才可以這么查找妇多。這種查找方式效率要比逐條查詢快很多。

6. 優(yōu)化未緩存搜索

這是一種指數(shù)查找的方式燕侠,具體方法文中只是給了個(gè)圖片顯示者祖,仔細(xì)看了下源碼,了解了一下指數(shù)查找方式贬循。

exponentialSearch(scrollTop) {
  let bound = 1;
  const data = this.data;
  const start = this.lastMeasuredIndex >= 0 ? this.lastMeasuredIndex : 0;
  while (start + bound < data.length && this.getItemSizeAndOffset(start + bound).offset < scrollTop) {
     bound = bound * 2;
  }
  return this.binarySearch(
    start + Math.floor(bound / 2), 
    Math.min(start + bound, data.length), 
    scrollTop
  );
},

仔細(xì)看下邏輯其實(shí)并不難:

  • 先獲取開(kāi)始查詢的位置 start咸包,從 lastMeasuredIndex 開(kāi)始查起。
  • 循環(huán)執(zhí)行 getItemSizeAndOffset 方法獲取偏移量與 ScrollTop 屬性對(duì)比杖虾。如果偏移量小于 ScrollTop烂瘫,則 bound 乘以2(其實(shí)就是指數(shù)加1),直到 start 索引值超出元素?cái)?shù)量或者偏移量大于 ScrollTop奇适,跳出循環(huán)坟比。
  • 跳出循環(huán)說(shuō)明在最后一個(gè)指數(shù)有我們需要的數(shù)據(jù),獲取跳出循環(huán)前的 start 索引(即除以2)嚷往,最后還是使用二分法去找最終結(jié)果葛账。

一些思考

最后作者也留下了幾個(gè)思考的問(wèn)題:

  1. 根據(jù)渲染結(jié)果動(dòng)態(tài)的更新列表項(xiàng)的高度
  2. 數(shù)據(jù)更新后讓緩存失效,并盡可能讓失效緩存的范圍最小

第一個(gè)問(wèn)題不太理解皮仁,何謂 渲染結(jié)果籍琳。
第二個(gè)問(wèn)題呢菲宴,說(shuō)下大概猜想:

  1. 既然是數(shù)據(jù)更新,那么通過(guò)數(shù)據(jù)監(jiān)聽(tīng)趋急、對(duì)比喝峦、差異化等方式將更新的數(shù)據(jù)的 index 找出來(lái)。
  2. 在 getItemSizeAndOffset 中獲取緩存方法前判斷 index 相對(duì) data 數(shù)據(jù)是否更新呜达,更新則不讀取緩存而是重新計(jì)算谣蠢,并且更新緩存內(nèi)容。

一些問(wèn)題

  • sizeAndOffsetCahce 緩存為何是對(duì)象而非數(shù)組查近,因?yàn)?/li>
  • 二分法查找數(shù)據(jù)中眉踱,返回 0 不返回 -1 是因?yàn)橹笠玫?slice 方法的關(guān)系吧?如果為 -1 會(huì)查找數(shù)組后一位的數(shù)據(jù)霜威。
  • 二分法查找數(shù)據(jù)中谈喳,if (low > 0){ index = low - 1 } 這段代碼的意義何在?

最后

看了下大牛的文章,收獲還是蠻多的侥祭。不過(guò)還是停留在接受信息的階段叁执,最好能夠在思考和理解之后輸出點(diǎn)內(nèi)容。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末矮冬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子次哈,更是在濱河造成了極大的恐慌胎署,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窑滞,死亡現(xiàn)場(chǎng)離奇詭異琼牧,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)哀卫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門巨坊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人此改,你說(shuō)我怎么就攤上這事趾撵。” “怎么了共啃?”我有些...
    開(kāi)封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵占调,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我移剪,道長(zhǎng)究珊,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任纵苛,我火速辦了婚禮剿涮,結(jié)果婚禮上言津,老公的妹妹穿的比我還像新娘。我一直安慰自己取试,他們只是感情好纺念,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著想括,像睡著了一般陷谱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瑟蜈,一...
    開(kāi)封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天烟逊,我揣著相機(jī)與錄音,去河邊找鬼铺根。 笑死宪躯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的位迂。 我是一名探鬼主播访雪,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掂林!你這毒婦竟也來(lái)了臣缀?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泻帮,失蹤者是張志新(化名)和其女友劉穎精置,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锣杂,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脂倦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了元莫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赖阻。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖踱蠢,靈堂內(nèi)的尸體忽然破棺而出火欧,到底是詐尸還是另有隱情,我是刑警寧澤朽基,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布布隔,位于F島的核電站,受9級(jí)特大地震影響稼虎,放射性物質(zhì)發(fā)生泄漏衅檀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一霎俩、第九天 我趴在偏房一處隱蔽的房頂上張望哀军。 院中可真熱鬧沉眶,春花似錦、人聲如沸杉适。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猿推。三九已至片习,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹬叭,已是汗流浹背藕咏。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秽五,地道東北人孽查。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像坦喘,于是被迫代替她去往敵國(guó)和親盲再。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 問(wèn)答題47 /72 常見(jiàn)瀏覽器兼容性問(wèn)題與解決方案瓣铣? 參考答案 (1)瀏覽器兼容問(wèn)題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,754評(píng)論 1 92
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理答朋,服務(wù)發(fā)現(xiàn),斷路器坯沪,智...
    卡卡羅2017閱讀 134,665評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,180評(píng)論 25 707
  • 去年局地蘋果绿映、梨等農(nóng)產(chǎn)品去庫(kù)存大戰(zhàn)還未結(jié)束,今年新一輪的果品銷售已拉開(kāi)帷幕腐晾。受這兩年來(lái)頻頻出現(xiàn)的農(nóng)產(chǎn)品滯銷影響,不...
    農(nóng)牧人閱讀 322評(píng)論 0 1
  • 突如其來(lái)的表白, 時(shí)間一點(diǎn)點(diǎn)流逝库车。 默默不語(yǔ)轉(zhuǎn)話題巨柒, 不是不喜歡你, 而是青春傷不起柠衍! 運(yùn)動(dòng)會(huì)仿若在即洋满, 如果不曾...
    玫瑰花的夢(mèng)閱讀 133評(píng)論 6 4