關(guān)于Vue?監(jiān)控數(shù)組的問題

常見面試題
- Vue 如何監(jiān)控數(shù)組
- defineProperty 真的不能監(jiān)測數(shù)組變化嗎?
Vue 是如何追蹤數(shù)據(jù)發(fā)生變化
在 Vue 中當(dāng)我們把一個普通的 JS 對象作為 data 傳入 Vue 實例,Vue2.x 對這個數(shù)據(jù)初始化時將遍歷這個對象所有的屬性,并使用 JS 的原生特性 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter\setter。這些 getter\setter 對用戶來說是不可見的,他們可以在屬性被訪問和修改時通知變更。同時每個組件實例都對應(yīng)一個 watcher 實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù)屬性記錄為依賴。之后當(dāng)依賴項的 setter 觸發(fā)時,會通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染。

Vue 如何更新數(shù)組
// 方法一: 使用 Vue.set Vue.set(vm.items, indexOfItem, newValue) // 方法二: 使用 Vue 可監(jiān)測的數(shù)組變異方法: Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue)
為什么有些數(shù)組的數(shù)據(jù)變更不能被 Vue 監(jiān)測到
簡單來說,我們操作數(shù)組的一些動作 arr[2] = 'xxx' / arr.length = 2 或者是調(diào)用 Array.prototype 上掛載的部分方法并不能觸發(fā)這個屬性的 setter。
在數(shù)組的更新中有提到,可以使用 Vue 可監(jiān)測的數(shù)組變異方法: Array.prototype.splice, 哪為什么這個方法可以觸發(fā)狀態(tài)的更新了。 這是因為 Vue2.x 將數(shù)組的 7 個常用方法 push、pop、shift、unshift、splice、sort、reverse 進行了重寫,所以通過調(diào)用包裝之后的數(shù)組方法就能夠被 Vue 監(jiān)測到。
// Vue 2.6.14
// src/core/observer/array.js
import { def } from '../util/index'
// 記錄原始 Array 未重寫之前的 API 原型方法
const arrayProto = Array.prototype
// 深拷貝一份上面的原型出來
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
* 攔截上邊數(shù)組中列出的變異方法, 并發(fā)出事件通知
*/
methodsToPatch.forEach(function (method) {
// cache original method
// 緩存 Array.prototype 中的同名原始方法
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
// 調(diào)用執(zhí)行原有的數(shù)組方法
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 如果是插入的數(shù)據(jù),將它再次監(jiān)聽起來
if (inserted) ob.observeArray(inserted)
// 觸發(fā)訂閱,像頁面更新響應(yīng)就在這里觸發(fā)
ob.dep.notify()
return result
})
})Vue 為什么不能通過下標(biāo)操作數(shù)組或者改變數(shù)組的長度來觸發(fā)視圖更新
那 Vue2.x 監(jiān)測數(shù)組變更的兩條限制:不能監(jiān)聽利用索引直接設(shè)置一個數(shù)組項,不能監(jiān)聽直接修改數(shù)組的長度,是因為 defineProperty 的限制么?
答案:是的
Object.defineProperty 對于數(shù)組變化監(jiān)聽的表現(xiàn)與 Vue2.x 還是有不同的,比如 Object.defineProperty 可以監(jiān)聽到通過索引直接修改數(shù)組項,當(dāng)然也不是說 Object.defineProperty 可以完全監(jiān)聽數(shù)組的變化,像直接修改數(shù)組的長度或者 push\pop 之類的方法還是不能觸發(fā) setter 的。
這里就會出現(xiàn)一個新的問題?
為什么 Object.defineProperty 明明能監(jiān)聽到數(shù)組值的變化,而 Vue 卻沒有實現(xiàn)呢?
這是因為 Vue 是對數(shù)組元素進行了監(jiān)聽,而沒有對數(shù)組本身的變化進行監(jiān)聽。
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
// 區(qū)分對象和數(shù)組,對象和數(shù)組走不通的響應(yīng)式方案
if (Array.isArray(value)) {
// 判斷是否支持__proto__屬性,根據(jù)不同的請求來添加數(shù)組的攔截方法
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
// 循環(huán)數(shù)組的元素,再次調(diào)用observe方法,
this.observeArray(value);
} else {
// 如果是對象,循環(huán)對象屬性,為對象屬性添加getter,setter方法,將屬性變成響應(yīng)式
this.walk(value);
}
};這其實是出于性能原因的考量,給每一個數(shù)組元素綁定上監(jiān)聽,實際消耗很大而受益并不大。
其實還有一些考慮是:對數(shù)據(jù)的操作更常用的操作數(shù)組的方法是使用數(shù)組原型上的一些方法如 push、shift 等來操作數(shù)組。Object.defineProperty是對象上的方法,用來對數(shù)組的下標(biāo)進行檢測,會改變數(shù)據(jù)本來的性質(zhì)。
總結(jié)來說:三點原因
- 性能原因的考量
- 對數(shù)據(jù)的操作更常用的操作數(shù)組的方法是使用數(shù)組原型上的一些方法如 push、shift 等來操作數(shù)組。
- Object.defineProperty是對象上的方法,用來對數(shù)組的下標(biāo)進行檢測,會改變數(shù)據(jù)本來的性質(zhì)。
當(dāng)然最重要的就是性能問題。

Vue 3.0 是如何處理的?
Vue3 不再采用 defineProperty 的方式來進行監(jiān)聽而是采用 Proxy 的方式。下面我引用了 MDN 上對于 proxy 的介紹: Proxy 對象用于創(chuàng)建一個對象的代理,從而實現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。 當(dāng)異步觸發(fā) Model 里的數(shù)據(jù)變化時,都會經(jīng)過 Proxy 這一層,在這里則可以監(jiān)聽數(shù)組以及各種數(shù)據(jù)類型的變化,無論是數(shù)組下標(biāo)賦值引起變化還是數(shù)組方法引起變化,都可以被監(jiān)聽到,也可以避開監(jiān)聽數(shù)組每個屬性下造成的性能問題。
參考
- cn.vuejs.org/v2/guide/re…
- www.jianshu.com/p/fc8da283c…
- baijiahao.baidu.com/s?id=163912…
- blog.csdn.net/XH_jing/art…
- developer.mozilla.org/zh-CN/docs/…
到此這篇關(guān)于Vue 監(jiān)控數(shù)組的示例詳解的文章就介紹到這了,更多相關(guān)Vue 監(jiān)控數(shù)組內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue3使用hook封裝媒體查詢和事件監(jiān)聽的代碼示例
這篇文章主要給大家詳細(xì)介紹Vue3如何使用hook封裝媒體查詢和事件監(jiān)聽,使得Vue的開發(fā)更加絲滑,文中通過代碼示例給大家介紹的非常詳細(xì),感興趣的同學(xué)跟著小編一起來學(xué)習(xí)吧2023-07-07
vue如何實現(xiàn)拖動圖片進行排序Vue.Draggable
這篇文章主要介紹了vue如何實現(xiàn)拖動圖片進行排序Vue.Draggable,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04
VUE使用echarts?5.0以上版本渲染器未導(dǎo)入錯誤問題
這篇文章主要介紹了VUE使用echarts?5.0以上版本渲染器未導(dǎo)入錯誤問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06
Vue Element前端應(yīng)用開發(fā)之界面語言國際化
我們開發(fā)的系統(tǒng),一般可以不用考慮語言國際化的問題,大多數(shù)系統(tǒng)一般是給本國人使用的,而且直接使用中文開發(fā)界面會更加迅速 一些,不過框架最好能夠支持國際化的處理,以便在需要的時候,可以花點時間來實現(xiàn)多語言切換的處理,使系統(tǒng)具有更廣泛的受眾用戶。2021-05-05
vue router導(dǎo)航守衛(wèi)(router.beforeEach())的使用詳解
導(dǎo)航守衛(wèi)主要用來通過跳轉(zhuǎn)或取消的方式守衛(wèi)導(dǎo)航。這篇文章主要介紹了vue-router導(dǎo)航守衛(wèi)(router.beforeEach())的使用,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04
vue2.0實現(xiàn)點擊其他區(qū)域關(guān)閉自定義div功能
這篇文章主要介紹了vue2.0實現(xiàn)點擊其他區(qū)域關(guān)閉自定義div功能實現(xiàn),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06

