目錄
Vue2
Object.defineProperty()
defineProperty()是Object的靜態方法,透過其可以在物件上新增屬性,或修改物件的屬性
它接收3個參數分別為obj、property、descriptor
其中descriptor會是物件,分為data descriptors
(帶有value、writable屬性的物件)、accessor descriptors
(帶有getter和setter的物件)
注意2種不能混用
,不然會得到"Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"
If a descriptor has neither of value, writable, get and set keys, it is treated as a data descriptor. If a descriptor has both [value or writable] and [get or set] keys, an exception is thrown.
const foo = {};
Object.defineProperties(foo, {
a:{ // writable預設為false
value:33
},
b:{ // data descriptors,所以不能有getter和setter
value:32,
writable:true
},
})
console.log(foo.a, foo.b); // 33, 32
foo.a = 102;
foo.b = 101;
console.log(foo.a, foo.b); // 33, 101
響應式系統
響應式系統是透過Object.defineProperty()實現
的,之所以它是因為要支援一些老瀏覽器(ex:IE8)
export default Vue.extend({
name: 'Test',
data(){
return{
count:33,
}
},
beforeCreate(){
console.log(this.count); // undefined
},
created(){
console.log(this.count); // 33
}
})
在beforeCreate
階段,Vue
實例(instance)已被初始化
,但options還不能使用
Called immediately when the instance is initialized, after props resolution, before processing other options such as data() or computed
在created
階段options會被設置好
,所以可以透過this讀取count的值了。但$el還沒未被設為#app或組件的template
在beforeCreate和created間會初始化響應系統(init reactivity),這時會做類似以下的動作
class Foo{
constructor(options){
this.initReactivity(options.data());
}
// 初始化響應系統
initReactivity(data){
const keys = Object.getOwnPropertyNames(data);
keys.forEach(key => {
Object.defineProperty(this, key, {
configurable:true,
get(){
console.log('get');
return data[key];
},
set(val){
console.log('set');
if(val === data[key]) return;
data[key] = val;
}
})
})
}
}
// 實例已被建立
const foo = new Foo({
data(){
return {
count:33
}
}
});
console.log(foo.count); // 33
foo.count = 25; // 先get,後set
console.log(foo.count); // 25
但無法監聽陣列元素、物件屬性的增刪
、新增的屬性的變化。Map、Set、Class等,也不行
const foo = new Foo({
data(){
return {
person:{
name:'Emma',
},
arr:[1]
}
}
});
foo.arr[0] = 222; // get
foo.person.age = 12; // get
foo.num = 50; // 都沒有觸發
新手常見的畫面沒有隨資料改變重新渲染也是這個緣故
Vue3
Proxy
透過Proxy可以建立一個物件來替代原本的物件
,Proxy常用於屬性的存取、驗證
它接收
2個參數
分別為target、handler
(又稱作trap),前者為原本的物件,後者為一個用於定義何時攔截、攔截時會做甚麼的物件
const person = {
name:'alex',
}
const px = new Proxy(person, {
get(obj, prop, val){
console.log(`get ${prop}`);
return obj[prop];
},
set(obj, prop, val){ // obj為原物件, prop為屬性名
console.log(`set ${prop}`);
obj[prop] = val;
}
});
console.log(px.name); // get name
px.name = 'emma'; // set name
px.age = 25; // set name
Reflect
Reflect與Proxy用途相當類似,但它能接收第三個參數(receiver)
recevier
可以視為是指定函式其中的this的指向
const obj = {
get foo(){
return this.foo;
}
}
// 將this的指向改為第三個參數
console.log(Reflect.get(obj, 'foo', {foo:33})); // 33
不過Reflect並不能改變
原本就有指向的this的指向
const obj = {
foo:1,
get bar(){
return this.foo;
}
}
console.log(Reflect.get(obj, 'foo', {foo:33})); // 1
Proxy加上Reflect可以完成一個簡單的響應式系統
再詳細我也不清楚了QQ
參考資料
MDN Object.defineProperty()
MDN - Proxy
一文帶你深入剖析vue3的響應式
聽說你很瞭解 Vue3 響應式?