Lassen Sie uns zunächst die Implementierung des responsiven Systems ursprünglicher Werte verstehen Frischen Sie Ihre Proxy-Funktionen auf!
const obj = { name: 'win' } const handler = { get: function(target, key){ console.log('get--', key) return Reflect.get(...arguments) }, set: function(target, key, value){ console.log('set--', key, '=', value) return Reflect.set(...arguments) } } const data = new Proxy(obj, handler) data.name = 'ten' console.log(data.name,'data.name22')
Im obigen Code haben wir festgestellt, dass die Verwendung von Proxy selbst das Abfangen von Objekten durch den Rückgabewert von new Proxy
ist Auf diese Weise wird beim Zugriff auf den Wert im Objekt die Methode get
ausgelöst. Wenn Sie den Wert im Objekt ändern, wird die Methode set
ausgelöst. Aber wenn es um den ursprünglichen Wert geht, wird die Methode set
ausgelöst. Es gibt kein Objekt. Was soll ich tun? neuer Proxy
kann nicht mehr verwendet werden. In unserer Verzweiflung können wir es nur einpacken, also verwenden wir .value
, um auf new Proxy
的返回值,拦截了obj 对象如此一来,当你 访问对象中的值的时候,他会触发 get
方法, 当你修改对象中的值的时候 他会触发 set
方法但是到了原始值的时候,他没有对象啊,咋办呢,new proxy
排不上用场了。无奈之下,我们只能包装一下了,所以就有了使用.value
访问了
我们来看看具体实现:
import { reactive } from "./reactive"; import { trackEffects, triggerEffects } from './effect' export const isObject = (value) => { return typeof value === 'object' && value !== null } // 将对象转化为响应式的 function toReactive(value) { return isObject(value) ? reactive(value) : value } class RefImpl { public _value; public dep = new Set; // 依赖收集 public __v_isRef = true; // 是ref的标识 // rawValue 传递进来的值 constructor(public rawValue, public _shallow) { // 1、判断如果是对象 使用reactive将对象转为响应式的 // 浅ref不需要再次代理 this._value = _shallow ? rawValue : toReactive(rawValue); } get value() { // 取值的时候依赖收集 trackEffects(this.dep) return this._value; } set value(newVal) { if (newVal !== this.rawValue) { // 2、set的值不等于初始值 判断新值是否是对象 进行赋值 this._value = this._shallow ? newVal : toReactive(newVal); // 赋值完 将初始值变为本次的 this.rawValue = newVal triggerEffects(this.dep) } } }
上述代码,就是对于原始值,的包装,他被包装为一个对象,通过get value
和set value
方法来进行原始值的访问,从而导致必须有.value
的操作 ,这其实也是个无奈的选择
相当于两瓶毒药,你得选一瓶 鱼与熊掌不可兼得
第一个问题终于整明白了,那么我们来看看最重要的第二个问题,为什么结构赋值,会破坏响应式特性
在开始之前,我们先来讨论一下为什么要更改响应式方案
vue2 基于Object.defineProperty ,但是他有很多缺陷,比如 无法监听数组基于下标的修改,不支持 Map、Set、WeakMap 和 WeakSet等缺陷 ,
其实这些也也不耽误我们开发, vue2到现在还是主流,
我的理解就是与时俱进
, 新一代的版本,一定要紧跟语言的特性,一定要符合新时代的书写风格
,虽然proxy
相对于Object.defineProperty 有很多进步, 但是也不是一点缺点都没有,你比如说 不兼容IE
天底下的事情,哪有完美的呢?尤大的魄力就在于,舍弃一点现在,博一个未来!
在理解了背景之后,我们再来假模假式的温习一下proxy
原理,虽然这个都被讲烂了。
但是,写水文,讲究什么:俩字-连贯
const obj = { count: 1 }; const proxy = new Proxy(obj, { get(target, key, receiver) { console.log("这里是get"); return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { console.log("这里是set"); return Reflect.set(target, key, value, receiver); } }); console.log(proxy) console.log(proxy.count)
以上代码就是Proxy的具体使用方式,通过和Reflect 的配合, 就能实现对于对象的拦截
如此依赖,就能实现响应式了,大家可以发现,这个obj的整个对象就被拦截了,但是你发现对象在嵌套深一层
比如:
const obj = { count: 1, b: { c: 2 } }; console.log(proxy.b) console.log(proxy.b.c)
他就无法拦截了,我们必须要来个包装
const obj = { a: { count: 1 } }; function reactive(obj) { return new Proxy(obj, { get(target, key, receiver) { console.log("这里是get"); // 判断如果是个对象在包装一次,实现深层嵌套的响应式 if (typeof target[key] === "object") { return reactive(target[key]); }; return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { console.log("这里是set"); return Reflect.set(target, key, value, receiver); } }); }; const proxy = reactive(obj);
好了,原理搞完了,我们来正式研究一下现在列举一下我知道的响应式失去的几个情况:
1、解构 props
对象,因为它会失去响应式
2、 直接赋值reactive
响应式对象
3、 vuex
中组合API赋值
const obj = { a: { count: 1 }, b: 1 }; //reactive 是上文中的reactive const proxy = reactive(obj); const { a, b } = proxy; console.log(a) console.log(b) console.log(a.count)
上述代码中,我们发现, 解构赋值,b 不会触发响应式
,a如果你访问的时候
,会触发响应式
这是为什么呢?别急我们一个个解释?先来讨论为什么解构赋值,会丢失响应式呢?我们知道解构赋值,区分原始类型的赋值,和引用类型的赋值,
原始类型的赋值相当于按值传递, 引用类型的值就相当于按引用传递
就相当于:
// 假设a是个响应式对象 const a={ b:1} // c 此时就是一个值跟当前的a 已经不沾边了 const c=a.b // 你直接访问c就相当于直接访问这个值 也就绕过了 a 对象的get ,也就像原文中说的失去响应式
那为啥a
具备响应式呢?
因为a
是引用类型,我们还记得上述代码中的一个判断吗。如果他是个object
那么就重新包装为响应式
正式由于当前特性,导致,如果是引用类型, 你再去访问其中的内容的时候并不会失去响应式
// 假设a是个响应式对象 const a={ b:{c:3}} // 当你访问a.b的时候就已经重新初始化响应式了,此时的c就已经是个代理的对象 const c=a.b // 你直接访问c就相当于访问一个响应式对象,所以并不会失去响应式
以上就大致解释了为什么解构赋值,可能会失去响应式,我猜的文档中懒得解释其中缘由,索性就定了个规矩,您啊!
就别用了,省的以为是vue
const vue = reactive({ a: 1 }) vue = { b: 2 }
get value
und set value
verarbeitet Der Zugriff erfordert die Operation .value
. Dies ist eigentlich eine hilflose Wahl. 🎜🎜#proxy
große Fortschritte gemacht hat Im Vergleich zu Object.defineProperty gibt es überhaupt keine Mängel. Zum Beispiel nicht kompatibel mit IE
#🎜🎜##🎜🎜# Wie kann irgendetwas auf der Welt perfekt sein? Youdas Mut liegt darin, ein wenig von der Gegenwart aufzugeben und sich eine Zukunft zu sichern! #🎜🎜##🎜🎜#Implementierungsprinzip#🎜🎜##🎜🎜#Nachdem wir den Hintergrund verstanden haben, schauen wir uns das proxy
-Prinzip auf falsche Weise an, obwohl darüber schlecht gesprochen wurde. #🎜🎜##🎜🎜#Was jedoch beim Schreiben von Hydrologie wichtig ist: Zwei-Wörter-Kohärenz#🎜🎜#// 当reactive 之后返回一个代理对象的地址被vue 存起来, // 用一个不恰当的比喻来说,就是这个地址具备响应式的能力 const vue = reactive({ a: 1 }) // 而当你对于vue重新赋值的时候不是将新的对象赋值给那个地址,而是将vue 换了个新地址 // 而此时新地址不具备响应式,可不就失去响应式了吗 vue = { b: 2 }
import { computed } from 'vue' import { useStore } from 'vuex' export default { setup () { const store = useStore() return { // 在 computed 函数中访问 state count: computed(() => store.state.count), // 在 computed 函数中访问 getter double: computed(() => store.getters.double) } } }
props
Objekt, da es die Reaktionsfähigkeit verliert#🎜🎜#reaktives
responsives Objekt#🎜🎜#vuex
#🎜🎜#b keine Reaktionsfähigkeit auslöst
, a wird ausgelöst, wenn Sie darauf zugreifen
Responsive # 🎜🎜##🎜🎜#Warum ist das so? Machen Sie sich keine Sorgen, lassen Sie uns jeden einzelnen erklären? Lassen Sie uns zunächst diskutieren, warum die Reaktivität bei der Destrukturierung von Zuweisungen verloren geht. Wir wissen, dass die Destrukturierungszuweisung zwischen der ursprünglichen Typzuweisung und der Referenztypzuweisung unterscheidet. Die ursprüngliche Typzuweisung entspricht der Übergabe als Wert, und der Wert des Referenztyps entspricht der Übergabe als Referenz. 🎜 🎜##🎜🎜#Es ist äquivalent zu: #🎜🎜#rrreee#🎜🎜#Warum reagiert dann a
?#🎜🎜# # 🎜🎜#Da a
ein Referenztyp ist, erinnern wir uns noch an ein Urteil im obigen Code? Wenn es sich um ein Objekt
handelt, packen Sie es in ein responsives Objekt um. #🎜🎜##🎜🎜# Wenn es sich um einen Referenztyp handelt, können Sie offiziell nicht darauf zugreifen der Inhalt, wenn Sie darauf zugreifen. #🎜🎜#rrreee#🎜🎜#Das oben Gesagte erklärt grob, warum die Destrukturierung der Zuweisung zu einem Verlust der Reaktionsfähigkeit führen kann, also ist es einfach eine Regel , Du! #🎜🎜##🎜🎜#Verwenden Sie es nicht, sonst denken Sie, es sei ein Fehler von vue
und ändern die Nutzungsgewohnheiten des Benutzers im Voraus! Nicht daran gewöhnt #🎜🎜##🎜🎜# Reaktive reaktionsfähige Objekte direkt zuweisen#🎜🎜##🎜🎜#Als wir vue3 zum ersten Mal verwendeten, gaben wir an, dass wir den folgenden Code schreiben würden#🎜🎜#const vue = reactive({ a: 1 }) vue = { b: 2 }
然后就发出疑问reactive
不是响应式的吗? 为啥我赋值了以后,他的响应式就没了 ,接着破口大骂,垃圾vue
其实啊,这就是您对于js 原生的概念不清除,其实尤大
已经做了最大的努力,来防止你进行错误操作了
比如,由于解构赋值的问题, 他直接禁止了reactive的解构赋值
当你用解构赋值操作的时候,他直接禁用了那有人又问了, 为啥props 不给禁用了呢?因为你的props 的数据可能不是响应式的啊,不是响应式的,我得能啊,尤大他也不能干涉用户使用新语法啊
所以还是那句话:框架现在的呈现,其实充满了取舍,有时候真是两瓶毒药,挑一瓶!
回归正题,我们再来说说 原生js 语法,首先需要确认的是,原生js 的引用类型的赋值,其实是 按照引用地址赋值!
// 当reactive 之后返回一个代理对象的地址被vue 存起来, // 用一个不恰当的比喻来说,就是这个地址具备响应式的能力 const vue = reactive({ a: 1 }) // 而当你对于vue重新赋值的时候不是将新的对象赋值给那个地址,而是将vue 换了个新地址 // 而此时新地址不具备响应式,可不就失去响应式了吗 vue = { b: 2 }
在这里我要替,尤大说句公道话,人家又没收你钱,还因为他,你有口饭吃,您自己不能与时俱进,拥抱新事物,那是您没能耐
,这是典型的端起碗吃肉,放下筷子骂娘
在vuex 用赋值也可能会失去响应式:
import { computed } from 'vue' import { useStore } from 'vuex' export default { setup () { const store = useStore() return { // 在 computed 函数中访问 state count: computed(() => store.state.count), // 在 computed 函数中访问 getter double: computed(() => store.getters.double) } } }
以上代码中我们发现store.getters.double
必须用computed
包裹起来,其实道理是一样的,也是变量赋值的原因,在这里我们就不再赘述!
Das obige ist der detaillierte Inhalt vonSo lösen Sie das Problem, das durch den Verlust der Reaktionsfähigkeit bei der Strukturzuweisung in Vue3 verursacht wird. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!