最近在開發一套組件庫,期間在實現InputNumber 組件時候碰到一個詭異卡頓的現象,用了時間來排除這個問題,涉及到一些問題定位的方法,記錄下來已備後用。javascript
在實現 InputNumber 組件的時候,有一個功能是按住 + 或 - 按鈕時,組件的值在不斷的自增或者自減,具體以下圖html
當組件的值自增到必定數量以後,組件會開始卡頓,而且頁面上下滾動也會有明顯的延遲。vue
問題體驗java
相關代碼node
<script> export default { name: "input-number", ... methods: { handleClick(type) { const { step } = this; const period = 10; const timerHandle = () => { const { addDisabled, decDisabled } = this; if (!addDisabled && type === "add") this.inputNumberValue += step; if (!decDisabled && type === "dec") this.inputNumberValue -= step; }; const timer = setInterval(timerHandle, period); const startTime = new Date(); const handler = () => { const endTime = new Date(); if (endTime - startTime < period) timerHandle(); clearInterval(timer); document.removeEventListener("mouseup", handler, false); }; document.addEventListener("mouseup", handler, false); } ... }; </script>
複製代碼
首先定位問題發生的位置,直觀上感覺應該是點擊以後不無故自增發生的卡頓,對應代碼中的 handleClick
函數,它將 click
事件分爲 mousedown
以及 mouseup
,當觸發 mousedown
事件時候,調用一個 setInterval
定時執行組件值變化的函數。git
初步定位問題應該就發生在 timerHandle
以後,當 inputNumberValue
發生變化以後,它會按照必定的規則來改變 inputValue
的值,從而觸發 $emit(input, this.inputValue)
來完成 v-model
。github
computed: {
inputNumberValue: {
get() {
return this.inputValue;
},
set(value) {
// ...必定規則
this.inputValue = limits.find(limit => limit.need(value)).value;
}
}
},
watch: {
value: {
handler(newVal) {
console.timeEnd()
this.inputNumberValue = newVal;
},
immediate: true
},
inputValue(newVal) {
this.$emit("input", newVal);
}
}
複製代碼
利用 console.time
以及 console.timeEnd
來排查,那一步發生的卡頓,檢測整個 v-model
變化的流程。函數
也就是在 timerHandle
以及 watch value handler
內添加 console.time
以及 console.timeEnd
,具體以下工具
const timerHandle = () => {
const { addDisabled, decDisabled } = this;
if (!addDisabled && type === "add") this.inputNumberValue += step;
if (!decDisabled && type === "dec") this.inputNumberValue -= step;
console.time();
};
watch: {
value: {
handler(newVal) {
console.timeEnd()
this.inputNumberValue = newVal;
},
immediate: true
}
}
複製代碼
而後運行,發現運行時間是在不斷地增長的,這時候問題的能夠歸類爲,inputNumber 組件的值在不斷地變更,致使的 update
的時間會不斷地增加。post
接下來要判斷具體是哪一句js致使整個頁面的 update
時間不斷地變長,利用 Chrome 的 JavaScript Profiler 來完成該工做。打開開發者工具
利用這個面板你能夠追蹤網頁程序的內存泄漏問題,進一步提高程序的JavaScript執行性能,點擊Start 按鈕,而後去復現剛纔的操做,獲得結果以下
圖中標識處有三個模式:
選擇 Tree(Top Down) 模式,獲得結果以下
能夠看出 flushCallbacksvue
函數佔用了74.66%的 Total Time,因此須要對它進行分析
在它的調用棧中,關鍵的一步是 Vue._update
,它的主要功能是將 Vnode
渲染成真實DOM,因此上述的卡頓問題果真出如今渲染這一步。
繼續分析,發現主要問題在與 updateDirctives
這個函數內,看來問題和指令的更新相關。
最後,發現原來是 highlightBlock
的鍋,由於要完成頁面中代碼高亮的需求,開發了一個指令
import hljs from 'highlight.js/lib/highlight';
Vue.directive ('highlight', function (el) {
let blocks = el.querySelectorAll ('code');
Array.prototype.forEach.call (blocks, block => {
hljs.highlightBlock (block);
});
});
複製代碼
當 InputNumber 組件 v-model
所綁定的父組件 data
變更時候,會致使 v-highlight
指令不斷地更新,使得頁面卡頓。
只須要將該指令的高亮代碼的函數寫在 bind
裏面,這樣就只調用一次,指令第一次綁定到元素時調用。
Vue.directive ('highlight', {
bind (el) {
let blocks = el.querySelectorAll ('code');
Array.prototype.forEach.call (blocks, block => {
hljs.highlightBlock (block);
});
}
});
複製代碼
原創聲明: 該文章爲原創文章,轉載請註明出處。