Home > Article > Web Front-end > How to solve the problem caused by the loss of responsiveness in structure assignment in Vue3
Before understanding the implementation of the responsive system of original values, let’s first review the capabilities of 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')
In the above code, we found that the use of proxy itself is the interception of the object. Through the return value of new Proxy
, the obj object is intercepted. In this way, when you access the object When the value is reached, it will trigger the get
method. When you modify the value in the object, it will trigger the set
method. But when it comes to the original value, it has no object, so what? What to do, new proxy
can no longer be used. In desperation, we can only wrap it, so we use .value
to access
Let’s take a look at the specific implementation:
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) } } }
The above code is the packaging of the original value. It is packaged as an object. The original value is accessed through the get value
and set value
methods, which results in the necessity of .value
Operation, this is actually a helpless choice
It is equivalent to two bottles of poison, you have to choose one. You can’t have your cake and eat it too
The first question is finally clear, so let’s take a look at the second most important question, why structure assignment will destroy its responsiveness
Before we start, let’s first discuss why we need to change the responsive scheme
vue2 is based on Object.defineProperty, but it has many flaws, such as Cannot monitor the modification of arrays based on subscripts, and does not support defects such as Map, Set, WeakMap and WeakSet ,
In fact, these do not delay our development, vue2 is still mainstream now,
My understanding is that Advance with the times
, The new generation version must keep up with the characteristics of the language and must conform to the writing style of the new era
, although proxy
Compared with Object.defineProperty, there are many improvements, but it is not without any shortcomings. For example, is not compatible with IE
How can there be perfection in the world? What about? Youda's courage lies in giving up a little of the present and gaining a future!
After understanding the background, let’s review the proxy
principle in a fake way, although this has been badly explained.
However, what is important when writing hydrology: two words - continuity
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)
The above code is the specific use of Proxy. By cooperating with Reflect, the interception of objects can be achieved
With such dependence, responsiveness can be achieved. You can find that the entire object of obj is intercepted, but you find that the object is nested one layer deeper
For example:
const obj = { count: 1, b: { c: 2 } }; console.log(proxy.b) console.log(proxy.b.c)
He cannot intercept, we must come up with a package
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);
Okay, the principle is done, let’s study it formally and now list it Here are a few situations where responsiveness is lost that I know of:
1. Deconstruct the props
object because it will lose responsiveness
2. Direct assignmentreactive
Responsive object
3. vuex
Combined API assignment
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)
In the above code, we found that destructuring assignment, b will not trigger responsiveness
, aIf you visit
, the responsive
is triggered. Why is this? Don't worry, let's explain each one? Let’s first discuss why reactivity is lost when destructuring assignments? We know that destructuring assignment distinguishes between original type assignment and reference type assignment.
The original type assignment is equivalent to passing by value, and the value of reference type is equivalent to passing by reference.
is equivalent to:
// 假设a是个响应式对象 const a={ b:1} // c 此时就是一个值跟当前的a 已经不沾边了 const c=a.b // 你直接访问c就相当于直接访问这个值 也就绕过了 a 对象的get ,也就像原文中说的失去响应式
Then why is a
responsive?
Because a
is Reference type, do we still remember a judgment in the above code? If it is an object
then repackage it as responsive
Officially due to the current characteristics, if it is a reference type, you will not lose the responsiveness when accessing the content.
// 假设a是个响应式对象 const a={ b:{c:3}} // 当你访问a.b的时候就已经重新初始化响应式了,此时的c就已经是个代理的对象 const c=a.b // 你直接访问c就相当于访问一个响应式对象,所以并不会失去响应式
The above roughly explains why destructuring assignment may lose responsiveness. I guess the document is too lazy to explain the reason, so it simply makes a rule, you!
Don’t use it, otherwise you will think it is a vue
bug, and change the user’s usage habits in advance! Not used to it
When we first used vue3, we specified that the following code would be written
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
包裹起来,其实道理是一样的,也是变量赋值的原因,在这里我们就不再赘述!
The above is the detailed content of How to solve the problem caused by the loss of responsiveness in structure assignment in Vue3. For more information, please follow other related articles on the PHP Chinese website!