vue針對(duì)滾動(dòng)元素內(nèi)部大量元素财剖,但只有部分元素可見,對(duì)dom懶渲染癌淮,節(jié)省內(nèi)存的優(yōu)化

我們開發(fā)過程中經(jīng)常會(huì)遇到這樣的需求躺坟,一個(gè)數(shù)據(jù)量很大的列表。
遇到乳蓄,如果你有一個(gè)列表咪橙,
我們以百度信息流為例子


image.png

頁面打開,加載第一頁的數(shù)據(jù)虚倒,每次往下滾屏到接近底部美侦,會(huì)加載下一屏,這樣也是保證獲取數(shù)據(jù)的http請(qǐng)求量是按需加載的魂奥。圖片懶加載菠剩,也能節(jié)省流量的資源。但是捧弃,隨著我們一直加載,產(chǎn)生的dom會(huì)越來越多擦囊。內(nèi)存占用也會(huì)越來越高


image.png

還有我們常見的列表违霞,很多情況下我們通過分頁來加載,但是有些情況下瞬场,比如不能使用分頁买鸽,這時(shí)候如果有千上萬的數(shù)據(jù),就會(huì)產(chǎn)生大量的虛擬dom和真實(shí)dom贯被,大量占用內(nèi)存


c86bf1c1-c4e1-4103-a1e4-bc15c3d5ef61.gif

如果說上面的列表我們還能通過分頁解決眼五,那么Select選擇框妆艘,如果有成千上萬的選項(xiàng),即使我們使用了滾動(dòng)加載看幼,滾動(dòng)到最后也還是會(huì)產(chǎn)生大量的dom批旺,我本地測(cè)試了1萬條選項(xiàng)的下拉框,卡頓顯現(xiàn)非常明顯诵姜,點(diǎn)擊后需要2s才能展開選項(xiàng)汽煮,并且我的筆記本風(fēng)扇已經(jīng)開始呼呼排風(fēng)了。


image.png

image.png

這三類問題都有一個(gè)相似點(diǎn)棚唆,大量的并列的dom暇赤,但是這些dom都在一個(gè)可滾動(dòng)的容器中,無論怎么滾動(dòng)宵凌,只有一部分是可見的鞋囊,


未標(biāo)題-1.jpg

所以如果只有可見的數(shù)據(jù)才去創(chuàng)建虛擬dom,就會(huì)大量節(jié)省內(nèi)存瞎惫,并加快渲染速度溜腐。

我們以列表來作為例子,展示解決方案


image.png
<template>
    <div class="list" style="height: 300px;overflow: scroll">
        <div v-for="(item,index) in list" :key="index" class="item">
            <div v-html="item.id"></div>
            <div v-html="item.name"></div>
            <div v-html="item.createTime"></div>
            <div>
                <Button>修改</Button>
                <Button>刪除</Button>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                list: [
                    {
                        id: 1,
                        name: 'name' + 1,
                        createTime: '2018-09-09'
                    }, {
                        id: 2,
                        name: 'name' + 2,
                        createTime: '2018-09-09'
                    }, {
                        id: 3,
                        name: 'name' + 3,
                        createTime: '2018-09-09'
                    }, {
                        id: 3,
                        name: 'name' + 3,
                        createTime: '2018-09-09'
                    }
                ]
            }
        }
    }
</script>

我們做一點(diǎn)改動(dòng)微饥,通過代碼生成一個(gè)10000條的list

<template>
    <div class="list" style="height: 300px;overflow: scroll">
        <div v-for="(item,index) in list" :key="index" class="item">
            <div v-html="item.id"></div>
            <div v-html="item.name"></div>
            <div v-html="item.createTime"></div>
            <div>
                <Button>修改</Button>
                <Button>刪除</Button>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        created() {
            for (let i = 0; i < 10000; i++) {
                this.list.push({
                    id: 1,
                    name: 'name' + i,
                    createTime: '2018-09-09'
                })
            }
        },
        data() {
            return {
                list: []
            }
        }
    }
</script>

根據(jù)這個(gè)想法設(shè)計(jì)的解決方案逗扒,思路有幾個(gè)關(guān)鍵點(diǎn)。
1欠橘、假滾動(dòng)事件:拖動(dòng)滾動(dòng)滑塊矩肩,并不會(huì)影響左側(cè)的真實(shí)滾動(dòng),只是記錄滾動(dòng)的位置肃续。
2黍檩、根據(jù)滾動(dòng)的位置,計(jì)算出對(duì)應(yīng)的數(shù)據(jù)區(qū)間段始锚,比如應(yīng)該取340-350這10條刽酱。
3、根據(jù)滾動(dòng)位置瞧捌,和數(shù)據(jù)區(qū)間棵里,把這幾條數(shù)據(jù)控制絕對(duì)定位,來模擬滾動(dòng)效果姐呐。

第一步:假滾動(dòng)事件

<template>
    <div class="list" style="height: 300px;overflow: scroll">
        <div :style="{height:list.length*40+'px'}"></div>
    </div>
</template>

我們的每一行高度是40殿怜,那么我們創(chuàng)建一個(gè)高度時(shí)list.length*40的空元素,那么這個(gè)空元素的高度曙砂,跟展示所有的list的高度一樣头谜,也就是說右側(cè)的滾動(dòng)條效果跟原來的效果是一樣的。


image.png

第二步鸠澈,根據(jù)滾動(dòng)的位置柱告,計(jì)算出對(duì)應(yīng)的數(shù)據(jù)區(qū)間段

我們需要計(jì)算開始index和長度截驮,然后截取list數(shù)據(jù)的一部分

<template>
    <div class="list" style="height: 300px;overflow: scroll" ref="scrollDom" @scroll="scroll">
        <div :style="{height:list.length*40+'px'}"></div>
    </div>
</template>

<script>
    export default {
        created() {
            for (let i = 0; i < 10000; i++) {
                this.list.push({
                    id: 1,
                    name: 'name' + i,
                    createTime: '2018-09-09'
                })
            }
        },
        computed: {
            limitCount() {
                return 300 / 40;
            }
        },
        data() {
            return {
                startIndex:0,
                list: []
            }
        },
        methods: {
            scroll() {
                // 根據(jù)滾動(dòng)的距離,估算出這個(gè)滾動(dòng)位置對(duì)應(yīng)的數(shù)組序列际度,例如滾動(dòng)100px葵袭,每條40px,對(duì)應(yīng)第3條
                let scrollTop = this.$refs.scrollDom.scrollTop;
                this.startIndex = Math.floor(scrollTop / 40);
                console.log(this.startIndex);
            }
        }
    }
</script>
de5555a3-508a-40ca-bef2-199b58d322df.gif

我們根據(jù)開始和結(jié)束需要甲脏,生成一個(gè)splitData眶熬,然后再把新生成的dom通過絕對(duì)位置調(diào)整。


image.png

完成的效果


4fd819ec-9f0b-49e3-b904-51cd9ac10688.gif

我們可以看到块请,無論怎么滾動(dòng)娜氏,效果跟全部加載是一樣的,但是通過右側(cè)的控制臺(tái)發(fā)現(xiàn)墩新,dom數(shù)量只有固定的這幾個(gè)贸弥,但是每個(gè)dom內(nèi)部的值在不停的修改。

完整的代碼

<template>
    <div class="list" style="height: 300px;overflow: scroll" ref="scrollDom" @scroll="scroll">
        <div :style="{height:list.length*40+'px'}"></div>
        <div style="position:absolute;width: 100%" :style="{top:startIndex*40+'px'}">
            <div v-for="(item,index) in splitData" :key="index" class="item">
                <div v-html="item.id"></div>
                <div v-html="item.name"></div>
                <div v-html="item.createTime"></div>
                <div>
                    <Button>修改</Button>
                    <Button>刪除</Button>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        created() {
            for (let i = 0; i < 10000; i++) {
                this.list.push({
                    id: 1,
                    name: 'name' + i,
                    createTime: '2018-09-09'
                })
            }
        },
        computed: {
            limitCount() {
                return 300 / 40 + 2;
            },
            splitData() {
                return this.list.slice(this.startIndex, this.startIndex + this.limitCount)
            }
        },
        data() {
            return {
                startIndex: 0,
                list: []
            }
        },
        methods: {
            scroll() {
                // 根據(jù)滾動(dòng)的距離海渊,估算出這個(gè)滾動(dòng)位置對(duì)應(yīng)的數(shù)組序列绵疲,例如滾動(dòng)100px,每條40px臣疑,對(duì)應(yīng)第3條
                let scrollTop = this.$refs.scrollDom.scrollTop;
                this.startIndex = Math.floor(scrollTop / 40);
            }
        }
    }
</script>
<style scoped lang="less">
    .list {
        border: solid 1px #5f5f5f;
        background-color: white;
        margin: 100px 0;
        padding: 5px;
        width: 500px;
        position: relative;

        .item {
            display: flex;
            height: 40px;

            > * {
                flex-grow: 1;
                border: solid 1px #9e9e9e;
                padding: 3px;
            }
        }
    }
</style>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盔憨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子讯沈,更是在濱河造成了極大的恐慌郁岩,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缺狠,死亡現(xiàn)場(chǎng)離奇詭異问慎,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)挤茄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門如叼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人穷劈,你說我怎么就攤上這事笼恰。” “怎么了歇终?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵社证,是天一觀的道長。 經(jīng)常有香客問我练湿,道長猴仑,這世上最難降的妖魔是什么审轮? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任肥哎,我火速辦了婚禮辽俗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘篡诽。我一直安慰自己崖飘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布杈女。 她就那樣靜靜地躺著朱浴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪达椰。 梳的紋絲不亂的頭發(fā)上翰蠢,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音啰劲,去河邊找鬼梁沧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蝇裤,可吹牛的內(nèi)容都是我干的廷支。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼栓辜,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼恋拍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起藕甩,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤施敢,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辛萍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悯姊,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年贩毕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悯许。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辉阶,死狀恐怖先壕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谆甜,我是刑警寧澤垃僚,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站规辱,受9級(jí)特大地震影響谆棺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜罕袋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一改淑、第九天 我趴在偏房一處隱蔽的房頂上張望碍岔。 院中可真熱鬧,春花似錦朵夏、人聲如沸蔼啦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捏肢。三九已至,卻和暖如春饥侵,著一層夾襖步出監(jiān)牢的瞬間鸵赫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國打工躏升, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奉瘤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓煮甥,卻偏偏與公主長得像盗温,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子成肘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355