面试题 vue2 Object.definProperty响应式原理

注意:
响应式原理和双向数据绑定原理是两回事,一般面试官会先问响应式原理再问双向数据绑定的原理
详细文章
1.响应式原理
核心是数据劫持和依赖收集,是通过数据劫持结合发布者-订阅者模式的方式来实现的 。通过.()为对象添加属性,然后为对象设置和方法,之后我们每次通过点语法获取属性就会执行方法,在这个方法中我们会把调用 此属性的依赖收集到一个集合中;而在我们给属性赋值(修改属性)时,会触发方法,然后去通知集合中的依赖更新,做到数据变更 驱动视图变更 。
Object.defineProperty(obj, key, {// writable:true,// 可写// enumerable: true, // 可枚举// configurable: true, //可删除//拦截get,当我们访问data.key时会被这个方法拦截到get () {//我们在这里收集依赖return obj[key];},//拦截set,当我们为data.key赋值时会被这个方法拦截到set (newVal) {//当数据变更时,通知依赖项变更UI} })
2. MVVM模式
MVVM框架的的核心就是双向绑定, 其原理是通过数据劫持+发布订阅模式相结合的方式来实现的,简单来说就是数据层发生变化的时候,可同步更新视图层,当视图层发生变化的时候,同步更新数据层
3. 如何实现? Vue 类
首先我们实现一个 Vue 类,用于创建 vue对象,它的构造方法接收一个参数,用于初始化vue
class Vue {constructor(options){this.$el = options.el;this._data = options.data;this.$data = this._data// 对data进行响应式处理new Observe(this._data)}}// 创建 vue 对象new Vue ({el:'#app',data:{message:'hello world'}})
上面的代码中我们首先创建了一个Vue的类,构造函数跟我们平时使用的Vue大致一致,为了容易理解我们这里只处理了参数el和data 。
我们发现构造函数的最后一行创建了一个类的对象,并传入data作为参数,这里的就是对data数据进行响应式处理的类,接下来我们看一下类的简单实现 。

(数据监听器:能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者)
我们在类中实现对data的监听,就是通过.()方法实现的数据劫持,代码如下:
class Observe {constructor(data){// 如果传入的数据是objectif(typeof data =http://www.kingceram.com/post/= 'object'){this.walk(data);}}// 这个方法遍历对象中的属性,并依次对其进行响应式处理walk(obj){// 获取所有属性const keys = Object.keys(obj);for ( let i = 0; i < keys.length; i++ ){// 对所有属性进行监听(数据劫持)this.defineReactive(obj,keys[i])}}defineReactive(obj,key){if (typeof obj[key] == 'object'){// 如果属性是对象,那么递归调用walk方法this.walk(obj[key]);}const dep = new Dep(); // Dep类用于收集依赖const val = obj[key];Object.defineProperty(obj,key,{enumerable:true,configurable:true,// get代理将Dep.target 即watcher对象添加到依赖集合中get(){// 这里在创建watcher对象时 会给Dep.target赋值if(Dep.target){dep.addSubs(Dep.target);}return val;},set(newVal){val = newVal;// 依赖的变更响应dep.notify(newVal);}})}}
上述代码中我们使用到了Dep类,我们在劫持到的数据的get方法中收集到的依赖会被放到Dep类中保存 。
Dep类
订阅器:订阅器采用 发布-订阅 设计模式,用来收集订阅者 ,对监听器和 订阅者进行统一管理 。
下面代码是Dep类的实现,他有一个subs的数组,用于保存依赖,这里的依赖是我们后面要定义的,即观察者

面试题  vue2 Object.definProperty响应式原理

文章插图