Vue中使用MouseMove事件 獲取滑鼠座標頻率降低或事件卡頓
當我們使用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!如果你不希望有快取,請用方法來替代。
總結:如果能用計算屬性滿足需求優先使用,如果使用方法需注意方法執行時長
專案原始碼: ofollow,noindex">github.com/MrDerry/blo…