Vue 最獨特的特性之一蛉鹿,是其非侵入性的響應(yīng)式系統(tǒng)妖异。數(shù)據(jù)模型僅僅是普通的 JavaScript 對象日川。而當你修改它們時龄句,視圖會進行更新散罕。
前置知識
Vue對Data做了什么
getter 和 setter
let obj0 = {
姓: "高",
名: "圓圓",
age: 18
};
// 需求一欧漱,得到姓名
let obj1 = {
姓: "高",
名: "圓圓",
姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log("需求一:" + obj1.姓名());
// 姓名后面的括號能刪掉嗎误甚?不能窑邦,因為它是函數(shù)
// 怎么去掉括號擅威?
// 需求二,姓名不要括號也能得出值
let obj2 = {
姓: "高",
名: "圓圓",
get 姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log("需求二:" + obj2.姓名);
// 總結(jié):getter 就是這樣用的冈钦。不加括號的函數(shù)郊丛,僅此而已。
// 需求三:姓名可以被寫
let obj3 = {
姓: "高",
名: "圓圓",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx) {
this.姓 = xxx[0]
this.名 = xxx.slice(1)
},
age: 18
};
obj3.姓名 = '高媛媛'
console.log(`需求三:姓 ${obj3.姓}瞧筛,名 ${obj3.名}`)
// 總結(jié):setter 就是這樣用的厉熟。用 = xxx 觸發(fā) set 函數(shù)
Object.defineProperty()
之前在使用getter、setter時较幌,是在定義這個對象時直接使用的揍瑟。在定義完一個對象之后,要想再添加新的get set時只能用Object.defineProperty()
// 需求:n不能小于0
let data2 = {}
data2._n = 0 //用_n存儲n的值
Object.defineProperty(data2, 'n', {
get(){
return this._n
},
set(value){
//set可以添加判斷:小于0直接return,否則將n值置為最新value
if(value < 0) return
this._n = value
}
})
代理和監(jiān)聽
let myData5 = { n: 0 };
let data5 = proxy2({ data: myData5 }); // 括號里是匿名對象乍炉,無法訪問
function proxy2({ data } ) {
let value = data.n;
Object.defineProperty(data, "n", {
get() {
return value;
},
set(newValue) {
if (newValue < 0) return;
value = newValue;
}
});
// 就加了上面幾句月培,這幾句話會監(jiān)聽 data
const obj = {};
Object.defineProperty(obj, "n", {
get() {
return data.n;
},
set(value) {
if (value < 0) return; //這句話多余了
data.n = value;
}
});
return obj; // obj 就是代理
}
Object.defineProperty
- 可以給對象添加屬性value
- 可以給對象添加getter/setter
- getter/setter用于對屬性的讀寫進行監(jiān)控
什么是代理(設(shè)計模式)
- 對myData對象的屬性讀寫,全權(quán)由另一個對象vm負責
- 那么vm就是myData的代理
- 比如myData.n不用此叠,偏要用vm.n來操作myData.n
vm = new Vue({data: myData})
一窗看、會讓 vm 成為 myData 的代理(proxy)
二软瞎、會對 myData 的所有屬性進行監(jiān)控 為什么要監(jiān)控,為了防止 myData 的屬性變了只锭, vm 不知道
vm 知道屬性變了就可以調(diào)用 render(data) UI = render(data)
總之對data做了任何修改纵顾,Vue必須知道才能做到響應(yīng)刷新
回到最初的問題,Vue對Data做了什么
1.Vue會對data后面的{n:0}進行竄改,給它加監(jiān)聽闷愤。
2.會新生成一個對象遭居,這個對象會代理篡改后的對象告丢。
Vue數(shù)據(jù)響應(yīng)式
響應(yīng)式:若一個物體對外界刺激有所反應(yīng)颅湘,那就是響應(yīng)式
Vue的data是響應(yīng)式
const vm=new Vue({data:{n:0}})
我如果修改vm.n,那么UI中的n就會響應(yīng)我
Vue 2通過Object.defineProperty來實現(xiàn)數(shù)據(jù)響應(yīng)式
Vue的data的bug
new Vue({
data: {
obj: {
a: 0 //obj.a會被Vue監(jiān)聽 & 代理
//b:undefined
}
},
template: `
<div>
{{obj.b}}
<button @click="setB">set b</button>
</div>
`,
methods: {
setB() {
this.obj.b = 1; //頁面中不會顯示1
//Vue.set(this.obj,'b'乙漓,1)
//this.$set(this.obj,'b',1)
}
}
}).$mount("#app");
頁面中不會顯示1涩蜘,因為Vue沒法監(jiān)聽一開始不存在的obj.b
解決辦法
1.把k都聲明好,后面不再加屬性 比如b:undefined
2.使用Vue.set
或者this.$set
set(this.obj,'b'秩贰,1)`
Vue.set和this.$set作用:
- 新增key
- 自動創(chuàng)建代理和監(jiān)聽(如果沒有創(chuàng)建過)
- 觸發(fā)UI更新(但不會立刻更新)
數(shù)組變異
如果data中有數(shù)組,沒法提前聲明key的情況
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
//this.array[3] = "d"; 頁面中不會顯示'd'
//this.$set(this.array,3,'d') 增加下標的方式實現(xiàn)添加
this.array.push('d')//尤雨溪的做法
console.log(this.array)
}
}
}).$mount("#app");
變更方法
Vue 將被偵聽的數(shù)組的變更方法進行了包裹培漏,所以它們也將會觸發(fā)視圖更新友鼻。這些被包裹過的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
Vue在這個對象僻爽,你以為這個是數(shù)組對象须板,你傳給Vue之后,Vue就會篡改這個數(shù)組柠横。它會在中間加一層原型搬俊。這個原型有7個方法扩淀,這7個方法跟以前是同名的但是代碼被尤雨溪改了,會幫你set(監(jiān)聽,每次push都會通知Vue)庆聘。也就是說push會做2件事情:調(diào)以前的push,調(diào)完后通知Vue添加監(jiān)聽和代理宴抚。
總結(jié)
1.對象中新增的key
Vue無法事先監(jiān)聽和代理
要使用set來新增key,創(chuàng)建監(jiān)聽和代理,更新UI
最好提前把屬性都寫出來常潮,不要新增key
但數(shù)組做不到「不新增key」
2.數(shù)組中新增的key
也可用set來新增key,更新UI
不過尤雨溪篡改了7個API方便你對數(shù)組進行增刪
這7個API會自動處理監(jiān)聽和代理岔留,并更新UI
結(jié)論:數(shù)組新增key最后通過7個API
對數(shù)據(jù)響應(yīng)式的理解
簡單說就是用戶更改數(shù)據(jù)時厕吉,視圖可以自動刷新班眯,頁面UI能夠響應(yīng)數(shù)據(jù)變化磁餐。Object.defineProperty()給數(shù)據(jù)對象添加value屬性伴箩,設(shè)置getter和setter監(jiān)控屬性的讀寫,這些 getter/setter 對用戶來說是不可見的,但是在內(nèi)部它們讓 Vue 能夠追蹤依賴芒炼,在屬性被訪問和修改時通知變更。并使用vm對象負責數(shù)據(jù)對象的代理,當屬性更新時鲜屏,調(diào)用render()更新也殖。