關(guān)于Vue?監(jiān)控?cái)?shù)組的問題
常見面試題
- Vue 如何監(jiān)控?cái)?shù)組
- defineProperty 真的不能監(jiān)測(cè)數(shù)組變化嗎?
Vue 是如何追蹤數(shù)據(jù)發(fā)生變化
在 Vue 中當(dāng)我們把一個(gè)普通的 JS 對(duì)象作為 data 傳入 Vue 實(shí)例,Vue2.x 對(duì)這個(gè)數(shù)據(jù)初始化時(shí)將遍歷這個(gè)對(duì)象所有的屬性,并使用 JS 的原生特性 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter\setter。這些 getter\setter 對(duì)用戶來說是不可見的,他們可以在屬性被訪問和修改時(shí)通知變更。同時(shí)每個(gè)組件實(shí)例都對(duì)應(yīng)一個(gè) watcher 實(shí)例,它會(huì)在組件渲染的過程中把“接觸”過的數(shù)據(jù)屬性記錄為依賴。之后當(dāng)依賴項(xiàng)的 setter 觸發(fā)時(shí),會(huì)通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染。
Vue 如何更新數(shù)組
// 方法一: 使用 Vue.set Vue.set(vm.items, indexOfItem, newValue) // 方法二: 使用 Vue 可監(jiān)測(cè)的數(shù)組變異方法: Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue)
為什么有些數(shù)組的數(shù)據(jù)變更不能被 Vue 監(jiān)測(cè)到
簡(jiǎn)單來說,我們操作數(shù)組的一些動(dòng)作 arr[2] = 'xxx' / arr.length = 2 或者是調(diào)用 Array.prototype 上掛載的部分方法并不能觸發(fā)這個(gè)屬性的 setter。
在數(shù)組的更新中有提到,可以使用 Vue 可監(jiān)測(cè)的數(shù)組變異方法: Array.prototype.splice, 哪為什么這個(gè)方法可以觸發(fā)狀態(tài)的更新了。 這是因?yàn)?Vue2.x 將數(shù)組的 7 個(gè)常用方法 push、pop、shift、unshift、splice、sort、reverse 進(jìn)行了重寫,所以通過調(diào)用包裝之后的數(shù)組方法就能夠被 Vue 監(jiān)測(cè)到。
// 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ù)組的長(zhǎng)度來觸發(fā)視圖更新
那 Vue2.x 監(jiān)測(cè)數(shù)組變更的兩條限制:不能監(jiān)聽利用索引直接設(shè)置一個(gè)數(shù)組項(xiàng),不能監(jiān)聽直接修改數(shù)組的長(zhǎng)度,是因?yàn)?defineProperty 的限制么?
答案:是的
Object.defineProperty 對(duì)于數(shù)組變化監(jiān)聽的表現(xiàn)與 Vue2.x 還是有不同的,比如 Object.defineProperty 可以監(jiān)聽到通過索引直接修改數(shù)組項(xiàng),當(dāng)然也不是說 Object.defineProperty 可以完全監(jiān)聽數(shù)組的變化,像直接修改數(shù)組的長(zhǎng)度或者 push\pop 之類的方法還是不能觸發(fā) setter 的。
這里就會(huì)出現(xiàn)一個(gè)新的問題?
為什么 Object.defineProperty 明明能監(jiān)聽到數(shù)組值的變化,而 Vue 卻沒有實(shí)現(xiàn)呢?
這是因?yàn)?Vue 是對(duì)數(shù)組元素進(jìn)行了監(jiān)聽,而沒有對(duì)數(shù)組本身的變化進(jìn)行監(jiān)聽。
var Observer = function Observer (value) { this.value = value; this.dep = new Dep(); this.vmCount = 0; def(value, '__ob__', this); // 區(qū)分對(duì)象和數(shù)組,對(duì)象和數(shù)組走不通的響應(yīng)式方案 if (Array.isArray(value)) { // 判斷是否支持__proto__屬性,根據(jù)不同的請(qǐng)求來添加數(shù)組的攔截方法 if (hasProto) { protoAugment(value, arrayMethods); } else { copyAugment(value, arrayMethods, arrayKeys); } // 循環(huán)數(shù)組的元素,再次調(diào)用observe方法, this.observeArray(value); } else { // 如果是對(duì)象,循環(huán)對(duì)象屬性,為對(duì)象屬性添加getter,setter方法,將屬性變成響應(yīng)式 this.walk(value); } };
這其實(shí)是出于性能原因的考量,給每一個(gè)數(shù)組元素綁定上監(jiān)聽,實(shí)際消耗很大而受益并不大。
其實(shí)還有一些考慮是:對(duì)數(shù)據(jù)的操作更常用的操作數(shù)組的方法是使用數(shù)組原型上的一些方法如 push、shift 等來操作數(shù)組。Object.defineProperty是對(duì)象上的方法,用來對(duì)數(shù)組的下標(biāo)進(jìn)行檢測(cè),會(huì)改變數(shù)據(jù)本來的性質(zhì)。
總結(jié)來說:三點(diǎn)原因
- 性能原因的考量
- 對(duì)數(shù)據(jù)的操作更常用的操作數(shù)組的方法是使用數(shù)組原型上的一些方法如 push、shift 等來操作數(shù)組。
- Object.defineProperty是對(duì)象上的方法,用來對(duì)數(shù)組的下標(biāo)進(jìn)行檢測(cè),會(huì)改變數(shù)據(jù)本來的性質(zhì)。
當(dāng)然最重要的就是性能問題。
Vue 3.0 是如何處理的?
Vue3 不再采用 defineProperty 的方式來進(jìn)行監(jiān)聽而是采用 Proxy 的方式。下面我引用了 MDN 上對(duì)于 proxy 的介紹: Proxy 對(duì)象用于創(chuàng)建一個(gè)對(duì)象的代理,從而實(shí)現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。 當(dāng)異步觸發(fā) Model 里的數(shù)據(jù)變化時(shí),都會(huì)經(jīng)過 Proxy 這一層,在這里則可以監(jiān)聽數(shù)組以及各種數(shù)據(jù)類型的變化,無論是數(shù)組下標(biāo)賦值引起變化還是數(shù)組方法引起變化,都可以被監(jiān)聽到,也可以避開監(jiān)聽數(shù)組每個(gè)屬性下造成的性能問題。
參考
- 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)控?cái)?shù)組的示例詳解的文章就介紹到這了,更多相關(guān)Vue 監(jiān)控?cái)?shù)組內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-admin-template框架搭建及應(yīng)用小結(jié)
?vue-admin-template是基于vue-element-admin的一套后臺(tái)管理系統(tǒng)基礎(chǔ)模板(最少精簡(jiǎn)版),可作為模板進(jìn)行二次開發(fā),這篇文章主要介紹了vue-admin-template框架搭建及應(yīng)用,需要的朋友可以參考下2023-05-05vue實(shí)現(xiàn)簡(jiǎn)單數(shù)據(jù)雙向綁定
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)簡(jiǎn)單數(shù)據(jù)雙向綁定,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04vue3利用store實(shí)現(xiàn)記錄滾動(dòng)位置的示例
這篇文章主要介紹了vue3利用store實(shí)現(xiàn)記錄滾動(dòng)位置的示例,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下2021-04-04Vue3中createWebHistory和createWebHashHistory的區(qū)別詳析
這篇文章主要給大家介紹了關(guān)于Vue3中createWebHistory和createWebHashHistory區(qū)別的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-06-06vue中的vue-router?query方式和params方式詳解
這篇文章主要介紹了vue中的vue-router?query方式和params方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08vue3+vite 動(dòng)態(tài)引用靜態(tài)資源及動(dòng)態(tài)引入assets文件夾圖片的多種方式
通過require動(dòng)態(tài)引入, 發(fā)現(xiàn)報(bào)錯(cuò):require is not defind,這是因?yàn)?nbsp;require 是屬于 Webpack 的方法,本文給大家介紹vue3+vite 動(dòng)態(tài)引用靜態(tài)資源及動(dòng)態(tài)引入assets文件夾圖片的多種方式,感興趣的朋友一起看看吧2023-10-10vue打包報(bào)錯(cuò):ERROR in static/js/xxx.js from U
這篇文章主要介紹了vue打包報(bào)錯(cuò):ERROR in static/js/xxx.js from UglifyJs undefined問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09淺談vuejs實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)視圖原理
這篇文章主要介紹了淺談vuejs實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)視圖原理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02