回答這個問題,實際上回答的是:Vue 的數據響應式原理有什麼限制?理解了 Vue 的數據響應式原理的限制,就能夠很好回答這個問題。數組
在實際使用 Vue 的過程當中,對於響應式 Data 的聲明,必須將須要響應的各個屬性都逐一在聲明階段寫清楚。若是對於一個響應式的 Object,動態增長了某一個屬性,那麼 Vue 是沒法爲該新增屬性作響應式處理的。舉個栗子:bash
export default {
data(){
return {
foo : 1,
bar : {tau : "test"}
}
}
//給data動態增長屬性newFoo, 頁面沒法響應式
this.data.newFoo = "123"
//給bar動態增長屬性tau2, 頁面沒法響應
this.data.bar.tau2 = "tell me"
複製代碼
理解了這個限制,咱們再來看看數組的狀況。咱們會常常對數組進行的操做是怎樣的呢?app
var array1 = ['t1','t2','t3']
//動態修改元素
array1[0] = 'tt1'
//動態增長元素
array1[4] = 't5'
//push方法增長數組元素
array1.push("1234")
//動態改變元素的長度
array1.length = 100
複製代碼
這些常規操做,若是隻是經過常規的攔截 key、value 的方式來進行數據響應式,那麼明顯沒法徹底覆蓋。而且成本過高,設想一下:若是用戶直接寫一個array1[1000] = 'haha'
,Vue 是否要爲其 1000 個子元素(極有多是都爲 null )都響應化處理呢?post
另外,值得注意的是,之因此對象能夠在 data 聲明的時候寫死,是由於頁面操做中的對象屬性基本上能夠在編寫的時候肯定。但頁面中數組的變化就不同,其變化基本上沒法預測。而 JS 語言中的數組對其子元素的屬性不限制,因而更加凌亂。ui
那麼,Vue 是採用了什麼方式來進行數組的數據響應化實現呢?this
咱們來接着上篇文章的內容,看源碼。spa
查看/src/observer/array.js
,能夠知道Vue對數組的七個方法進行了攔截處理:prototype
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
*/
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
//執行原始方法
const result = original.apply(this, args)
//額外通知變動,固然,只有這7個方法纔會有這個待遇
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
//對新加入對象進行響應化處理
if (inserted) ob.observeArray(inserted)
// notify change
//此處通知,能夠知道數組更新行爲
ob.dep.notify()
return result
})
})
複製代碼
咱們來看,在定義 Observer 的時候,如何處理數組:code
import { arrayMethods } from './array'
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
//值得留意的是:Observer對象在一個Vue實例中是存在多個的,取決於data數據中嵌套了幾個Object對象或數組對象
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
//若是是數組
if (Array.isArray(value)) {
//若是可以使用原型特性,直接將變異方法賦予響應化數組的原型鏈上
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
//若是沒法使用原型,那麼經過defineProperty的方式將變異方法賦予響應化數組
copyAugment(value, arrayMethods, arrayKeys)
}
//接着,對數組子元素,進行新一輪的observe數據響應化的過程
this.observeArray(value)
} else {
//若是是對象
this.walk(value)
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
function protoAugment (target, src: Object) {
target.__proto__ = src
}
function copyAugment (target: Object, src: Object, keys: Array<string>) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
def(target, key, src[key])
}
}
複製代碼
這個過程並不難看懂。至此,咱們能夠回答這個問題:server
data: {
obj: {foo: 'foo'}
bar: ['tua', 'tea']
}
//下面的這個操做,是否會觸發數據響應化過程呢?
this.bar[0] = 'testNew';
複製代碼