Maison > Article > interface Web > Comment résoudre le problème causé par la perte de réactivité dans l'affectation de structure dans Vue3
Pour comprendre la mise en œuvre du système réactif de valeurs originales, commençons par Révisez les capacités du proxy !
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')
Dans le code ci-dessus, nous avons constaté que l'utilisation du proxy lui-même est l'interception d'objets. Grâce à la valeur de retour de new Proxy
, l'objet obj est intercepté. de cette façon, lorsque vous accéderez à la valeur dans l'objet, cela déclenchera la méthode get
. Lorsque vous modifierez la valeur dans l'objet, cela déclenchera la méthode set
. Mais quand il s'agit de la valeur d'origine, cela déclenchera la méthode set
. Il n'y a pas d'objet, que dois-je faire ? Le nouveau proxy
ne peut plus être utilisé. En désespoir de cause, nous ne pouvons que l'envelopper, nous utilisons donc .value
pour accéder à 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
et set value
Value. l'accès nécessite l'opération de .value
. C'est en fait un choix impuissant équivaut à deux bouteilles de poison. Vous ne pouvez pas en choisir une. 🎜🎜#
Pourquoi la déconstruction ES6 ne peut pas être utilisée avec désinvolture pour détruire ses fonctionnalités réactives #🎜🎜##🎜🎜# La première question est enfin claire, alors jetons un œil à la plus importante La deuxième question, pourquoi structurer l'affectation détruira les caractéristiques réactives#🎜🎜##🎜🎜#fond de proxy#🎜🎜##🎜🎜#Avant de commencer, discutons de la raison pour laquelle nous devons changer le schéma réactif#🎜🎜 ##🎜🎜#vue2 est basé sur Object.defineProperty, mais il présente de nombreux défauts, tels que Il ne peut pas surveiller la modification du tableau en fonction de l'indice et il ne prend pas en charge les défauts Map, Set, WeakMap et WeakSet <.>La version nouvelle génération doit suivre les caractéristiques du langage et doit se conformer au style d'écriture de la nouvelle ère
Bien que proxy
ait fait beaucoup de progrès. par rapport à Object.defineProperty, ce n'est pas du tout Il n'y a aucun défaut Par exemple, Non compatible avec IE
#🎜🎜##🎜🎜# Comment quelque chose dans le monde peut-il être parfait ? Le courage de Youda réside dans le fait d'abandonner un peu du présent et de conquérir un avenir ! #🎜🎜##🎜🎜#Principe de mise en œuvre#🎜🎜##🎜🎜#Après avoir compris le contexte, passons en revue le principe du proxy
d'une manière fausse, même si on en a mal parlé. #🎜🎜##🎜🎜#Cependant, ce qui est important lors de l'écriture d'hydrologie : deux mots-cohérence#🎜🎜#// 当reactive 之后返回一个代理对象的地址被vue 存起来, // 用一个不恰当的比喻来说,就是这个地址具备响应式的能力 const vue = reactive({ a: 1 }) // 而当你对于vue重新赋值的时候不是将新的对象赋值给那个地址,而是将vue 换了个新地址 // 而此时新地址不具备响应式,可不就失去响应式了吗 vue = { b: 2 }#🎜🎜#Le code ci-dessus est l'utilisation spécifique de Proxy En coopérant avec Reflect, les objets peuvent être réalisés Intercepter. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Avec une telle dépendance, la réactivité peut être obtenue. Vous pouvez constater que l'objet entier de l'objet est intercepté, mais vous constatez que l'objet est imbriqué une couche plus profondément#🎜🎜 ##🎜 🎜#Par exemple :#🎜🎜#
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) } } }#🎜🎜#Il ne peut pas intercepter, il faut trouver un colis #🎜🎜#rrreee#🎜🎜#D'accord, le principe est terminé , étudions-le formellement. Listons maintenant quelques situations que je connais où la réactivité est perdue : #🎜🎜#
props.
, car il perdra sa réactivité#🎜🎜#réactif
responsive#🎜🎜#vuex
#🎜🎜##🎜🎜##🎜🎜#Déconstruisez l'objet props car il perdra en réactivité#🎜🎜 #rrreee# 🎜🎜##🎜🎜##🎜🎜#Dans le code ci-dessus, nous avons constaté que l'affectation de déstructuration, b ne déclenchera pas de réactivité
, a se déclenchera si vous y accédez
Responsive # 🎜🎜##🎜🎜#Pourquoi ça ? Ne vous inquiétez pas, expliquons chacun ? Voyons d'abord pourquoi la réactivité est perdue lors de la déstructuration des missions ? Nous savons que l'affectation de déstructuration fait la distinction entre l'affectation de type d'origine et l'affectation de type de référence #🎜🎜##🎜🎜#L'affectation de type d'origine équivaut au passage par valeur, et la valeur du type de référence équivaut au passage par référence#. 🎜 🎜##🎜🎜#C'est équivalent à : #🎜🎜#rrreee#🎜🎜#Alors pourquoi
a
est-il responsive ?#🎜🎜# # 🎜🎜#Parce que a
est un type référence, nous souvenons-nous encore d'un jugement dans le code ci-dessus ? S'il s'agit d'un objet
, alors reconditionnez-le en un objet responsive #🎜🎜##🎜🎜# Officiellement en raison des caractéristiques actuelles, s'il s'agit d'un type référence, vous ne pourrez pas y accéder. le contenu lorsque vous y accédez. Perte de réactivité#🎜🎜#rrreee#🎜🎜#Ce qui précède explique en gros pourquoi la déstructuration de l'affectation peut entraîner une perte de réactivité. Je suppose que le document est trop paresseux pour en expliquer la raison, il constitue donc simplement une règle. , toi! #🎜🎜##🎜🎜#Ne l'utilisez pas, sinon vous penserez que c'est un bug de vue
et changerez les habitudes d'utilisation de l'utilisateur à l'avance ! Pas habitué à #🎜🎜##🎜🎜# Attribuer directement des objets réactifs réactifs#🎜🎜##🎜🎜#Lorsque nous avons utilisé vue3 pour la première fois, nous avons précisé que nous écririons le code suivant#🎜🎜#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
包裹起来,其实道理是一样的,也是变量赋值的原因,在这里我们就不再赘述!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!