vue2双向数据绑定原理

1.数据双绑原理

vue使用MVVM框架,MVVM框架核心是数据双绑。即通过数据劫持和发布订阅模式,使用Object.defineProperty()对数据的属性和子属性进行递归遍历并对其设置getter和setter方法,数据发生改变时,发布消息给订阅者,出发对应的监听回调渲染视图。达到数据更新则视图更新,视图更新则数据更新的效果。

2.数据双绑流程

主要涉及三个核心:Observe、Compile、Watcher。

1:new Vue()时首先执行初始化,并对data数据进行数据劫持(响应式处理),这个过程发生在Observe中。

2:同时对模板执行编译,找到其中动态绑定的数据,从data中获取并初始化视图,这个过程发生在Compile

3:同时定义一个dep()函数和Watcher,将来对应数据变化时Watcher会调用更新函数。

4:data数据发生变化时,首先找到对应的Dep,然后通知watcher执行更新函数

data中的属性,称为key,一个视图中key可能会出现多次;一个watcher对应一个key;相同key对应的watcher存在一个dep()中。即一个watcher对应一个key,但是一个dep()里面存放多个相同key的watcher

流程图如下: 

3.要点解析

2.1 Observe 观察者

通过Object.defineProperty()对数据进行数据劫持

class Vue {  
  constructor(options) {  
    this.$options = options;  
    this.$data = options.data;  
        
    // 对data选项做响应式处理  
    observe(this.$data);  
        
    // 代理data到vm上  
    proxy(this);  
        
    // 执行编译  
    new Compile(options.el, this);  
  }  
}  
Object.keys(obj).forEach((key) => {  
      defineReactive(obj, key, obj[key]);  
    });

2.2 Compile 编译模版

对每个元素节点及其子元素节点进行递归遍历,对元素上的节点和文本进行编译,把模板中的变量替换成数据,然后初始化渲染视图,同时把每个指令对应的节点绑定上更新函数,添加订阅者,如果数据变化,收到通知,更新视图

class Compile {  
  constructor(el, vm) {  
    this.$vm = vm;  
    this.$el = document.querySelector(el);  // 获取dom  
    if (this.$el) {  
      this.compile(this.$el);  
    }  
  }  
  compile(el) {  
    const childNodes = el.childNodes;   
    Array.from(childNodes).forEach((node) => { // 遍历子元素  
      if (this.isElement(node)) {   // 判断是否为节点  
        console.log("编译元素" + node.nodeName);  
      } else if (this.isInterpolation(node)) {  
        console.log("编译插值⽂本" + node.textContent);  // 判断是否为插值文本 {{}}  
      }  
      if (node.childNodes && node.childNodes.length > 0) {  // 判断是否有子元素  
        this.compile(node);  // 对子元素进行递归遍历  
      }  
    });  
  }  
  isElement(node) {  
    return node.nodeType == 1;  
  }  
  isInterpolation(node) {  
    return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);  
  }  
}  
  

2.3收集依赖,Watcher,Dep

 视图中会用到data中某key,称为依赖。同⼀个key可能出现多次,每次都需要收集出来用⼀个Watcher来维护它们,此过程称为依赖收集。多个Watcher需要⼀个Dep来管理,需要更新时由Dep统⼀通知。

 4.依赖收集实现思路:

  1. defineReactive时为每⼀个key创建⼀个Dep实例 (Observe) 
  2. 初始化视图时读取某个key,例如name1,创建⼀个watcher1(Compile) 
  3. 由于触发name1getter方法,便将watcher1添加到name1对应的Dep中(Observe中调用addDep())
  4. name1更新,setter触发时,便可通过对应Dep通知其管理所有Watcher更新

4.1 Watcher类

Watcher类负责更新视图:创建实例时,把当前实例指定到Dep.targer静态属性上;然后读取key,出发getter,再将Dep.target置空;当执行Dom更新函数时,由dep()调用进行Watcher进行更新

// 负责更新视图  
class Watcher {  
  constructor(vm, key, updater) {  
    this.vm = vm  
    this.key = key  
    this.updaterFn = updater  
  
    // 创建实例时,把当前实例指定到Dep.target静态属性上  
    Dep.target = this  
    // 读一下key,触发get  
    vm[key]  
    // 置空  
    Dep.target = null  
  }  
  
  // 未来执行dom更新函数,由dep调用的  
  update() {  
    this.updaterFn.call(this.vm, this.vm[this.key])  
  }  
}  

4.2声明Dep 

class Dep {  
  constructor() {  
    this.deps = [];  // 依赖管理  
  }  
  addDep(dep) {  
    this.deps.push(dep);  
  }  
  notify() {   
    this.deps.forEach((dep) => dep.update());  
  }  
}  

 4.3创建watcher时触发getter

class Watcher {  
  constructor(vm, key, updateFn) {  
    Dep.target = this;  
    this.vm[this.key];  
    Dep.target = null;  
  }  
}  
  

4.4Observe中创建Dep实例,并依赖收集

function defineReactive(obj, key, val) {  
  this.observe(val);  
  const dep = new Dep();  
  Object.defineProperty(obj, key, {  
    get() {  
      Dep.target && dep.addDep(Dep.target);// Dep.target也就是Watcher实例  
      return val;  
    },  
    set(newVal) {  
      if (newVal === val) return;  
      dep.notify(); // 通知dep执行更新方法  
    },  
  });  
}  

5:总结

Observe: 递归遍历data上的属性和子属性,并通过Object.defineProperty()进行数据劫持;同时创建dep实例,并在Object.defineProperty()的get()上将watcher设置进dep中

Compile: 编译解析模版指令,并将模版中的变量替换成数据,然后初始化渲染视图,并给每个节点指令绑定上更新函数,添加订阅者。当数据变化时,收到通知更新视图

依赖收集: 初始化渲染视图时就会触发属性的getter方法,并调用Dep中的addDep()将值设置进Dep中;而数据中每个属性key对应一个watcher,多个相同key的watcher存放在一个Dep中;数据变化时,会首先找到对应的Dep,通知Dep对应的所有Watcher执行更新函数

参考地址:

面试官:双向数据绑定是什么 | web前端面试 - 面试官系列

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue2中,双向数据绑定是通过Object.defineProperty()方法进行实现的。这个方法允许我们对数据对象(data)的属性进行监听,以便在属性值发生变化时触发相应的事件。具体来说,当我们在模板中使用了一个数据绑定的表达式,Vue会在内部创建一个Watcher对象来监视这个表达式所依赖的数据。当数据发生变化时,Watcher对象会收到通知并触发更新视图的操作。同时,当用户在页面上操作了一个表单元素时,Vue会通过相应的事件监听器捕获到这个变化,并将新的值反映回数据对象中。这种方式实现了数据双向绑定,使得数据的变化能够自动更新到视图中,同时用户的操作也能够及时地反映到数据对象中。这就是Vue2中双向数据绑定的原理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Vue2双向绑定原理](https://blog.csdn.net/h18377528386/article/details/127515469)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [详解Vue双向数据绑定原理解析](https://download.csdn.net/download/weixin_38621624/12773200)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值