當我們使用Vue進行專案開發時,因為Vue的簡介和易用性使我們可能會忽略,Vue的生命週期這件事兒。 尤其是在使用事件時,稍有不意就會造成意外發生!
本文章使用常見的拖拽為案例。
當拖拽一個div元素時,很明顯會造成滑鼠快速滑動時div跟隨卡頓
共通程式碼:
<script>
export default {
data() {
return {
// 測試資料
testData: [
{value: '1'},
{value: '2'},
{value: '3'},
{value: '4'},
{value: '5'},
{value: '6'},
{value: '7'},
{value: '9'},
{value: '10'}
],
/// ...
};
},
methods: {
testFun(name) {
console.time(name + '-delay');
for (let i = 0; i < 10240000; i++) {}
console.timeEnd(name + '-delay');
},
// ...
}
}
</script>
<style>
*::selection {
background: none;
}
.box {
position: fixed;
z-index: 100;
width: 200px;
height: 80px;
}
.dargbtn {
margin: 15px;
color: #222222;
background: #eee;
cursor: pointer;
}
.box1 {
background: #c0c;
}
.box2 {
background: #0cc;
}
</style>
複製程式碼
上述所示,testData是測試的資料(用於資料資料迴圈),testFun是測試的方法(此方法用於拉長函式執行時長), 以及Style。
Box1程式碼:
<template>
<div class="box box1"
:style="box1Style"
ref="box1"
>
<div class="dargbtn" @mousedown="box1ButtonDown">點此拖拽1</div>
<div class="delay-box">
<span
v-for="(item, index) in testData"
:key="index"
:data-testdata="testFun('box1')"
>{{item.value}}</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
// 1
box1X: 0,
box1Y: 0,
box1L: 0,
box1T: 0,
box1CurrentX: 0,
box1CurrentY: 0,
};
},
computed: {
box1Style() {
return {
top: this.box1CurrentY + 'px',
left: this.box1CurrentX + 'px'
};
}
},
methods: {
box1Start(e) {
let dv = this.$refs.box1;
this.box1X = e.clientX;
this.box1Y = e.clientY;
this.box1L = dv.offsetLeft;
this.box1T = dv.offsetTop;
},
box1Move(e) {
console.log('box1 move');
let nx = e.clientX;
let ny = e.clientY;
let nl = nx - (this.box1X - this.box1L);
let nt = ny - (this.box1Y - this.box1T);
// 程式碼關鍵處
this.box1CurrentX = nl;
this.box1CurrentY = nt;
},
box1End(e) {
window.removeEventListener('mousemove', this.box1Move);
window.removeEventListener('mouseup', this.box1End);
},
box1ButtonDown(e) {
this.box1Start(e);
window.addEventListener('mousemove', this.box1Move);
window.addEventListener('mouseup', this.box1End);
}
}
}
</script>
複製程式碼
Box2程式碼:
<template>
<div class="box box2"
:style="box2Style"
ref="box2"
>
<div class="dargbtn" @mousedown="box2ButtonDown">點此拖拽2</div>
<div class="delay-box">
<span
v-for="(item, index) in testData2"
:key="index"
:data-testdata="testFun('box2')"
>{{item.value}}</span>
</div>
</div>
</template>
<script>
export default {
data() {
return {
// 2
box2X: 0,
box2Y: 0,
box2L: 0,
box2T: 0,
box2CurrentX: 0,
box2CurrentY: 100
};
},
computed: {
box2Style() {
return {
top: '100px',
left: '0px'
};
}
},
methods: {
box2Start(e) {
let dv = this.$refs.box2;
this.box2X = e.clientX;
this.box2Y = e.clientY;
this.box2L = dv.offsetLeft;
this.box2T = dv.offsetTop;
},
box2Move(e) {
console.log('box2 move');
let nx = e.clientX;
let ny = e.clientY;
let nl = nx - (this.box2X - this.box2L);
let nt = ny - (this.box2Y - this.box2T);
// 程式碼關鍵處
this.box2CurrentX = nl;
this.box2CurrentY = nt;
let legendBox = this.$refs.box2;
legendBox.style.left = nl + 'px';
legendBox.style.top = nt + 'px';
},
box2End(e) {
window.removeEventListener('mousemove', this.box2Move);
window.removeEventListener('mouseup', this.box2End);
},
box2ButtonDown(e) {
this.box2Start(e);
window.addEventListener('mousemove', this.box2Move);
window.addEventListener('mouseup', this.box2End);
}
}
}
</script>
複製程式碼
執行程式碼如圖所示:
程式碼分析
上訴兩段程式碼中,我們發現唯一的差別只有 box1是通過computed的計算屬性對style賦值進行的賦值 box2是通過methos的Move方法對style賦值進行的賦值 但是實際問題不在於此,這也就是該程式碼的炸彈!
在Vue中資料繫結有兩種方式:計算屬性和方法
計算屬性快取 vs 方法
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在元件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
複製程式碼
我們可以將同一函式定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基於它們的依賴進行快取的。只在相關依賴發生改變時它們才會重新求值。這就意味著只要 message 還沒有發生改變,多次訪問 reversedMessage 計算屬性會立即返回之前的計算結果,而不必再次執行函式。
這也同樣意味著下面的計算屬性將不再更新,因為 Date.now() 不是響應式依賴:
computed: {
now: function () {
return Date.now()
}
}
複製程式碼
相比之下,每當觸發重新渲染時,呼叫方法將總會再次執行函式。
我們為什麼需要快取?假設我們有一個效能開銷比較大的計算屬性 A,它需要遍歷一個巨大的陣列並做大量的計算。然後我們可能有其他的計算屬性依賴於 A 。如果沒有快取,我們將不可避免的多次執行 A 的 getter!如果你不希望有快取,請用方法來替代。
總結
如果能用計算屬性滿足需求優先使用,如果使用方法需注意方法執行時長
例子:
本文提供demo見:GitHub
*版權宣告:本文為原創文章,未經允許不得轉載。