目錄
1、$set 、$delete
2、靜默重繪
3、proxy
4、Reflect
vue2的回應式原理是通過object.defineProperty來劫持各個屬性的setter和getter在資料發生變化的時候發布訊息給訂閱者,觸發回應的監聽回呼,這里不再對vue2回應原理做過多解釋,可以查看往期博客
在vue2中通過object.defineProperty實作回應式原理時新增屬性、洗掉屬性界面不會更新;直接通過下標修改陣列,界面也不會自動更新(vue2也做了相對解決$set,$delete);下面來具體介紹$set、$delete和靜默重繪
1、$set 、$delete
語法:
- vm.$set("被設定的物件" , "屬性/索引" , "值")
- vm.$delete("被設定的物件" , "屬性/索引")
下面看看直接添加、洗掉物件屬性(代碼僅供測驗,可忽略)
<template> <div> <p>姓名:{{obj.name}}</p> <p>年齡:{{obj.age}}</p> <p>性別:{{obj.sex}}</p> <button @click="addSex">添加性別sex</button> <button @click="delAge">洗掉年齡age</button> </div> </template> <script> export default { name: "", data() { return { obj:{ name:'zs', age:20 } }; }, created() {}, computed: {}, methods: { addSex(){ this.obj.sex = '男' console.log("添加性別sex",this.obj); }, delAge(){ delete this.obj.age console.log("洗掉年齡age",this.obj); } }, }; </script> <style lang="less" scoped></style>
可發現物件的sex屬性被添加了,age屬性被洗掉了,但是資料未被回應,界面沒有更新
![]()
可以看出資料的變化會影響到界面的更新
2、靜默重繪
addSex() {
this.flag = false;
this.obj.sex = "男"; //不是回應式
//this.$set(this.obj, "sex", "男"); //是回應式
console.log("添加性別sex", this.obj);
this.flag = true;
},
delAge() {
this.flag = false;
delete this.obj.age; //不是回應式
//this.$delete(this.obj, "age"); //是回應式
console.log("洗掉年齡age", this.obj);
this.flag = true;
},


可以看出靜默重繪利用v-if重新加載節點實作回應重繪頁面資料,這樣面對大量添加的屬性和未知屬性也是比較方便的
3、proxy
語法:const p = new proxy("新物件" , "函式物件")
下面看看vue3的新增和洗掉屬性會不會實作界面重繪
<template> <div> <p>姓名:{{ obj.name }}</p> <p>年齡:{{ obj.age }}</p> <p>性別:{{ obj.sex }}</p> <button @click="addSex">添加性別sex</button> <button @click="delAge">洗掉年齡age</button> </div> </template> <script> import { reactive } from "vue"; export default { name: "HelloWorld", setup() { let obj = reactive({ name: "zs", age: 20, }); function addSex() { obj.sex = "男"; console.log("添加性別sex", this.obj); } function delAge() { delete obj.age; console.log("洗掉年齡age", this.obj); } return { obj, addSex, delAge, }; }, }; </script> <style scoped></style>
可以看出vue3中物件屬性的增加和洗掉,界面會隨之更新,這是因為vue2的回應式為object.defineProperty而vue3是proxy搭配Reflect
//因為proxy就兩個引數,所以怎么判斷是哪個屬性呢 const p = new Proxy(obj, { get(target, prop) { //target:物件 prop:屬性 console.log("get:", target, prop); return prop; }, set(target, prop, value) { //target:物件 prop:屬性 value:新的值 console.log("set:", target, prop, value); return true; }, deleteProperty(target, prop) { //洗掉 console.log("delete:", target, prop); return delete target[prop]; }, });
接下來將代碼進行優化
<template> <div> <p>姓名:{{ obj.name }}</p> <p>年齡:{{ obj.age }}</p> <p>性別:{{ obj.sex }}</p> <button @click="putAge">修改年齡</button> <button @click="getName">讀取姓名</button> <button @click="delSex">洗掉性別</button> <button @click="addClass">增加班級</button> </div> </template> <script> import { reactive } from "vue"; export default { name: "HelloWorld", setup() { let obj = reactive({ name: "zs", age: 20, sex: "男", }); const p = new Proxy(obj, { get(target, prop) { console.log("get:", target, prop); return prop; }, set(target, prop, value) { target[prop] = value; console.log("set:", target, prop, value); console.log("有人修改了", prop); return true; }, deleteProperty(target, prop) { console.log("delete:", target, prop); console.log("有人洗掉了", prop); return delete target[prop]; }, }); function putAge() { p.age = 18; } function getName() { console.log("有人讀取了", p.name); } function delSex() { delete p.sex; } function addClass() { p.class = "www"; //增加物件屬性呼叫的是set } return { obj, putAge, getName, delSex, addClass, }; }, }; </script> <style scoped></style>下面做一下總結:
- 獲取屬性呼叫proxy中的get函式
- 修改和新增呼叫proxy中的set函式
- 洗掉呼叫的 proxy中的deleteProperty函式
而vue3中proxy是和Reflect搭配著使用的,下面來簡單介紹下Reflect
4、Reflect
Reflect直譯為映射,官方解釋它是一個內置物件,提供攔截js操作的方法,這些方法與proxy handlers的方法相同,Reflect不是一個函式物件,因此它是不可構造的,它的所有屬性和方法都是靜態的(和Math一樣),下面將代碼進行改進一下
<template> <div> <p>姓名:{{ obj.name }}</p> <p>年齡:{{ obj.age }}</p> <p>性別:{{ obj.sex }}</p> <p>班級:{{ obj.class }}</p> <button @click="putAge">修改年齡</button> <button @click="getName">讀取姓名</button> <button @click="delSex">洗掉性別</button> <button @click="addClass">增加班級</button> </div> </template> <script> import { reactive } from "vue"; export default { name: "HelloWorld", setup() { let obj = reactive({ name: "zs", age: 20, sex: "男", }); const p = new Proxy(obj, { get(target, prop) { console.log("get:", target, prop); return Reflect.get(target, prop); }, set(target, prop, value) { target[prop] = value; console.log("set:", target, prop, value); console.log("有人修改了", prop); return Reflect.set(target, prop, value); }, deleteProperty(target, prop) { console.log("delete:", target, prop); console.log("有人洗掉了", prop); return Reflect.deleteProperty(target, prop); }, }); function putAge() { p.age = 18; } function getName() { console.log("有人讀取了", p.name); } function delSex() { delete p.sex; } function addClass() { p.class = "www"; } return { obj, putAge, getName, delSex, addClass, }; }, }; </script> <style scoped></style>
那Reflect有什么特點和好處呢?
- 將object物件的一些明顯屬于語言內部的方法(比如object.defineProperty),放到Reflect物件上,現階段,某些方法同時在object和Reflect物件上部署,未來的新方法只部署在Reflect物件上,也就是說Reflect可以拿到內部方法
- 修改object方法的回傳結果,比如object.defineProperty在無法定義屬性時會拋出錯誤,可用try{...}catch{...}來捕獲錯誤訊息,而Reflect直接會回傳false
- 讓object操作都變成函式行為,某些object操作是命令式的,Reflect會將其變成函式式
- Reflect方法跟Proxy物件的方法是對應的,Proxy的方法在Reflect都能找到,盡管修改了Proxy默認行為,Reflect也是能夠獲取默認行為的
以上總結參考程式思維
總結:
- 通過Proxy(代理):攔截物件中任意屬性的變化,包括:屬性值的讀寫、屬性的添加、屬性的洗掉等
- 通過Reflect(反射):對被代理物件的屬性進行操作
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/375915.html
標籤:其他








