記 Vue 大型表單項目的一個性能問題

問題場景

身為一個表單表格工程師奶段,自然日復(fù)一日的寫著表單表格突硝,本以為已經(jīng)沒啥難點的時候轉(zhuǎn)眼間就來了一個有意思的情況,在超大量 數(shù)據(jù)綁定在 vue 的時候出現(xiàn)了表單操作起來卡頓的情況惦积。

這里先貼上本項目出現(xiàn)的情況演示的 github 上的地址墩崩,tag1.0.1(https://github.com/everlose/more-form-demo/tree/v1.0.1)

如圖所見珊泳,當(dāng)在 input 輸入數(shù)據(jù)的時候弥雹,連續(xù)輸入會感覺明顯的延遲。

那么缨叫,這到底是怎么回事椭符?

代碼

上述的表單數(shù)據(jù)項修改頻繁由后端返回,于是在前端需要渲染從后端返回的 68kb 的一個 JSON 數(shù)據(jù)串耻姥,包括所有配置表單項以及其可能的選項值销钝,數(shù)據(jù)見這里

核心渲染是有這么一段


????{{config.title}}



????????????class="basic-form-item"

????????????v-for="(item, itemIndex) in config.formItems"

????????????:key="itemIndex"

????????????:prop="item.code"

????????????:label="item.name"

????????????:required="item.required"

????????????:rules="item.rules">


????????????????v-if="item.type === 'radio'"

????????????????v-model="formData[item.code]">


????????????????????v-for="(option, radioIndex) in formOptions[item.optionCode]"

????????????????????:key="option.value"

????????????????????:label="option.value"

????????????????????:disabled="item.disabled">

????????????????????{{?option.label?}}




????????????????v-else-if="item.type === 'input'"

????????????????:class="{ longInput: item.isLongInput }"

????????????????:placeholder="item.placeholder || '請輸入'"

????????????????v-model="formData[item.code]"

????????????????:label="item.label"

????????????????:disabled="item.disabled"

????????????????:maxlength="item.maxLength">



????????????????v-else-if="item.type === 'select'"

????????????????v-model="formData[item.code]"

????????????????:disabled="item.disabled"

????????????????:placeholder="item.placeholder || '請選擇'">


????????????????????v-for="(option, optionsIndex) in formOptions[item.optionCode]"

????????????????????:key="option.value"

????????????????????:label="option.label"

????????????????????:value="option.value">





這就是一個簡單的雙層遍歷渲染所有表單配置項的模版代碼,其中的 formConfig 正是所有配置表單項咏闪,數(shù)據(jù)量極多曙搬。formOptions 掛載了所有表單選項值,也是動輒幾千項鸽嫂。

思路

正當(dāng)我對著這么高的操作延時發(fā)愁的時候纵装,組里一個大佬提醒我,可能是 Vue.prototype._update 這個觸發(fā)的太頻繁了据某。

我急忙找到這一段打了個斷點調(diào)試

Vue.prototype._update 這函數(shù)里觸發(fā)的是 VNode 虛擬節(jié)點的比對更新橡娄,打斷點調(diào)試后發(fā)現(xiàn)實際上這是一個循環(huán),在控制臺里輸出 this.$el 的時候能得到正在深度遍歷中的節(jié)點癣籽,沿著根結(jié)點 App(也是 formConfig 數(shù)據(jù)綁定的作用域) 開始直到具體觸發(fā)輸入的那個表單元素挽唉。

在本項目里是使用了遍歷輸出所有的表單元素,并且當(dāng)前組件的作用域是直接掛在根結(jié)點上的筷狼,是否就是這個遍歷引發(fā)了如此高的延時呢瓶籽?于是我找到上圖右側(cè)的調(diào)用堆棧,發(fā)現(xiàn)正是 flushSchedulerQueue 函數(shù)寫著一個 for 循環(huán)埂材。

在 flushSchedulerQueue 函數(shù)中的 for 循環(huán)里頭尾插入代碼來獲取耗費時間塑顺。

結(jié)果得知輸入時的延遲大概在 300ms 之上。

似乎問題就找到了俏险,flushSchedulerQueue 函數(shù)針對 data 中數(shù)據(jù)的修改把 watcher 推送進隊列里在更新严拒,這一循環(huán)耗費的時間比較長。

解決

其實早在調(diào)試 Vue.prototype._update 函數(shù)就初見端倪竖独,循環(huán)中的 this.$el 從當(dāng)前組件的根部開始深度遍歷裤唠,遍歷了太多次,那么只要想辦法縮小當(dāng)前組件所綁定的數(shù)據(jù)量就解決了莹痢。

于是核心代碼調(diào)整為



只是用一個 edit-form 包裹剛剛所有的 el-form-item 的渲染代碼就解決了种蘸,再次調(diào)試 Vue.prototype._update 得出遍歷節(jié)點 this.$el 已經(jīng)變?yōu)橄聢D所示的 div.edit-form 了墓赴,flushSchedulerQueue 函數(shù) for 循環(huán)的延遲也變?yōu)?10ms 左右

修復(fù)版的代碼在2.0.0的tag上,這里貼上鏈接(https://github.com/everlose/more-form-demo/tree/v2.0.0)

后記

本質(zhì)上這就是一個原則劈彪,最好不要在一個vue組件上直接綁定如此多的數(shù)據(jù)竣蹦,如果有大量數(shù)據(jù)請分多個組件綁定。這么淺嘗輒止實在讓人不夠盡興沧奴,于是這里貼上 Vue.prototype._update 前的關(guān)鍵部分調(diào)用堆棧以及其函數(shù)作用痘括。

找到項目中 node_modules 下的 vue.esm.js

# 往input里輸入將會觸發(fā)model data的更新

978?set:?function?reactiveSetter?(newVal)??

# 訂閱器dep是數(shù)據(jù)綁定和視圖更新的關(guān)鍵,這里觸發(fā)去通知相關(guān)視圖的更新

994?dep.notify();??

673?Dep.prototype.notify??

# notify函數(shù)里的subs實際上是Watcher對象的實例滔吠,這里觸發(fā)視圖更新操作

677?subs[i].update();?subs實際上是包裹watcher的數(shù)組??

3093?Watcher.prototype.update??

# 把watcher塞進一個隊列里纲菌,這里是和異步更新視圖有關(guān)。

3100?queueWatcher(this);??

2945?function?queueWatcher?(watcher)?push到隊列里??

# nextTick是具體做異步更新的部分

2963?nextTick(flushSchedulerQueue);??

1778?function?nextTick?(cb,?ctx)??

# 異步操作實際上是原生 H5 MessageChannel API 通道通信來推送消息來實現(xiàn)變化疮绷。

1738?port.postMessage(1);


# 注意在異步操作中翰舌,最終傳入的回調(diào)函數(shù)被執(zhí)行來進行下面視圖的更新。這里是執(zhí)行一個任務(wù)調(diào)度隊列的調(diào)度過程冬骚,需要循環(huán)遍歷椅贱。

2856?function?flushSchedulerQueue??

3108?Watcher.prototype.run??

# Evaluate the getter, and re-collect dependencies.

3043?Watcher.prototype.get??

# watcher中的getter的name就叫updateComponent,于是被執(zhí)行

2689?updateComponent??

2690?vm._update(vm._render(),?hydrating);??

# 進入vue的生命周期中的update函數(shù)

2548?Vue.prototype._update??

# patch做的是vnode的節(jié)點比對只冻,最終把新的vnode結(jié)構(gòu)渲染到具體視圖庇麦,不再多做描述。

2572?vm.$el?=?vm.__patch__(prevVnode,?vnode);??

貼上提供思路的大佬的github地址: https://github.com/answershuto

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喜德,一起剝皮案震驚了整個濱河市山橄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舍悯,老刑警劉巖航棱,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異萌衬,居然都是意外死亡饮醇,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門秕豫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驳阎,“玉大人,你說我怎么就攤上這事馁蒂。” “怎么了蜘腌?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵沫屡,是天一觀的道長。 經(jīng)常有香客問我撮珠,道長沮脖,這世上最難降的妖魔是什么金矛? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮勺届,結(jié)果婚禮上驶俊,老公的妹妹穿的比我還像新娘。我一直安慰自己免姿,他們只是感情好饼酿,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胚膊,像睡著了一般故俐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上紊婉,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天药版,我揣著相機與錄音,去河邊找鬼喻犁。 笑死槽片,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肢础。 我是一名探鬼主播还栓,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼乔妈!你這毒婦竟也來了蝙云?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤路召,失蹤者是張志新(化名)和其女友劉穎勃刨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體股淡,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡身隐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唯灵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贾铝。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖埠帕,靈堂內(nèi)的尸體忽然破棺而出垢揩,到底是詐尸還是另有隱情,我是刑警寧澤敛瓷,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布叁巨,位于F島的核電站,受9級特大地震影響呐籽,放射性物質(zhì)發(fā)生泄漏锋勺。R本人自食惡果不足惜蚀瘸,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望庶橱。 院中可真熱鬧贮勃,春花似錦、人聲如沸苏章。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽布近。三九已至垫释,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撑瞧,已是汗流浹背棵譬。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留预伺,地道東北人订咸。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像酬诀,于是被迫代替她去往敵國和親脏嚷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

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