Vue2的数据代理和数据劫持

请问以下写法对吗?会出现什么问题?

Vue2的数据代理和数据劫持

Vue2的数据代理和数据劫持

Vue2的数据代理和数据劫持

1、数据代理

       简单的说,数据代理是一种机制,使得我们可以通过一个对象来访问另一个对象的属性,并修改其属性。在Vue2中,这种数据代理体现在Vue实例(vm)对data配置项的处理上。当创建一个Vue实例时,data配置项中的属性会被代理到vm实例上,这样我们就可以通过vm实例直接访问和修改data中的属性。

       具体来说,Vue2通过Object.defineProperty方法为vm实例的每个属性添加getter和setter函数,从而实现数据的读取和修改。当访问vm实例的属性时,会触发getter函数,从而返回data中对应的属性值;当修改vm实例的属性时,会触发setter函数,从而更新data中对应的属性值。这种机制使得Vue能够实时地监听数据的变化,并在数据变化时触发相应的更新操作。

1.1数据代理的实现

基于 Object.defineProperty() 方法,具体可参考developer.mozilla.org/zh-CN/docs/…

基本语法:Object.defineProperty(obj, prop, descriptor)

其中:

  • obj:要定义属性的对象(目标对象)。
  • prop:要定义或修改的属性的名称(目标属性名)。
  • descriptor:一个对象,它定义了要定义或修改的属性的描述符。
  • 描述符对象包含了四个可选属性:value、writable、enumerable 和 configurable:
    • value:属性的值。如果未定义,属性的默认值为 undefined。
    • writable:如果为 true,则属性值可以被赋值运算符改变;否则它是只读的。
    • enumerable:如果为 true,则属性会出现在对象的枚举属性中;否则不会。
    • configurable:如果为 true,则该属性描述符的配置可以更改,同时该属性也可以从对应对象中删除;否则它是不可配置的。

简化版的模拟Vue2的数据代理

function Vue(options) {
  this._data = options.data;
  数据代// 实现理
  for (let key in this._data) {
    Object.defineProperty(this, key, {
      get: function() {
        return this._data[key];
      },
      set: function(value) {
        this._data[key] = value;
      }
    });
  }
}
const vm = new Vue({
  data: {
    message: "Hello, Vue!",
    auther:"wangfei",
  }
});
console.log(vm.message); // 通过数据代理访问属性
vm.message = "Hello, Vue 2.0!"; // 通过数据代理修改属性
console.log(vm._data.message); // 通过原始 data 访问属性(仍然可行)
console.log(vm.message); // 通过原始 data 访问属性(仍然可行)

Vue2的数据代理和数据劫持

2、数据劫持

       数据劫持是指在访问或修改对象的属性时,对这些操作进行拦截和监视,以便在属性发生变化时能够触发相关的操作。Vue2 通过在数据对象的属性上使用 Object.defineProperty 来实现数据劫持,每当访问属性或修改属性时,Vue 会触发相应的 get 和 set 拦截器,从而实现对数据变化的监听。通过数据劫持,Vue 能够在属性发生变化时自动触发视图的更新,从而实现了响应式的特性。

2.1 数据劫持的实现

简化版模拟数据劫持的例子

class Dep {  
    constructor() {  
        this.subs = []; // 存储订阅者的数组  
    }  
    // 添加订阅者  
    addSub(sub) {  
		if (!this.subs.includes(sub)) {  
           this.subs.push(sub);   
        }  				
    }   
    // 通知所有订阅者更新  
    notify() {  
        this.subs.forEach(sub => {  
            sub.update();  
        });  
    }  
}   
class Watcher {  
    constructor(expr, cb) {  
        this.expr = expr;  
        this.cb = cb;  
        this.value = this.get(); // 初始化时执行 get 方法,触发依赖收集  
    }  
  
    // 获取数据并收集依赖  
    get() {  
        Dep.target = this; // 将当前订阅者设置为全局目标  
        const value = this.expr(); // 假设 expr 是一个返回数据属性的函数  
        Dep.target = null; // 清理全局目标  
        return value;  
    }  
  
    // 更新方法,当数据变化时调用  
    update() {  
        this.value = this.get(); // 重新获取数据并触发依赖收集  
        this.cb(this.value); // 执行回调函数,更新视图或其他操作  
    }  
}  
  
// 模拟 Vue 的响应式系统  
function observer(obj) {  
    Object.keys(obj).forEach(key => {  
        let dep = new Dep(); // 为每个属性创建一个依赖  
        let value = obj[key];  
  
        Object.defineProperty(obj, key, {  
            enumerable: true,  
            configurable: true,  
            get() {  
                if (Dep.target) { // 如果存在全局目标(订阅者)  
                    dep.addSub(Dep.target); // 收集依赖  
                }  
                return value;  
            },  
            set(newVal) {  
                if (newVal === value) return;  
                value = newVal;  
                dep.notify(); // 通知所有订阅者更新  
            }  
        });  
    });  
}  
 debugger 
// 示例用法  
let data = { message: 'Hello Vue!' };  
observer(data);  
  
// 模拟一个订阅者,当 message 变化时,执行回调函数  
new Watcher(() => data.message, newValue => {  
    console.log(`Message updated to: ${newValue}`);  
});  
  
// 修改数据,触发更新  
data.message = 'Hello Vue 2!'; // 输出:Message updated to: Hello Vue 2!

上述例子中:

Dep 类用于存储订阅者并通知它们更新。

Watcher 类表示一个订阅者,它在初始化时执行 get 方法以触发依赖收集,并在数据变化时调用 update 方法进行更新。

observer 函数遍历对象的属性,并使用 Object.defineProperty 将它们转换为 getter/setter,从而在访问和修改属性时执行相应的逻辑。

当 Watcher 的 get 方法被调用时,它将 Dep.target 设置为当前实例,从而允许 Dep 收集这个订阅者。当数据属性的 getter 被触发时,如果 Dep.target 存在,则将该订阅者添加到 Dep 的 subs 数组中。

当数据属性的 setter 被触发时,Dep 通知其所有的订阅者执行 update 方法,从而更新视图或执行其他操作。

这个简化的例子展示了 Vue 2 中观察者、依赖收集和订阅者更新机制的基本工作原理。但是在真实的 Vue 2 实现中,这个过程会更加复杂,并且会涉及更多的优化和细节处理。

3、如何给data中的对象添加响应式属性

Vue2的数据代理和数据劫持

参考文档:
v2.cn.vuejs.org/v2/api/#Vue…v2.cn.vuejs.org/v2/guide/re…

原文链接:https://juejin.cn/post/7351708519176896546 作者:王小菲

(0)
上一篇 2024年3月30日 下午4:42
下一篇 2024年3月30日 下午4:49

相关推荐

发表回复

登录后才能评论