MVVM与Vue响应式的实现

Vue的响应式实现原理

MVVM

M:模型 ==》data中的数据

V:视图 ==》模板

VM:视图模型 ==》Vue实例对象

在这里插入图片描述

ViewModel是一个中间的桥梁将视图View与模型Model连接起来,ViewModel内部通过数据绑定,实现数据变化,视图发生更新变化,通过数据劫持实现的数据绑定;通过dom监听,实现事件触发,调用对应的回调函数,比如更新数据(数据变化了,视图就会更新–数据绑定)

Vue 的设计也受到了MVVM的启发,View对应的是dom,它的ViewModel对应的式Vue实例,,Model对应的是data对象;通过数据劫持来实现数据绑定;编译(事件指令)的时候添加事件监听

Vue的响应式原理

主要是:数据代理、数据绑定、模板编译

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iKZWJxqy-1670162086242)(C:\Users\lucas\Desktop\学习\图片\reactive.jpg)]

绿线是初始化时执行的,红线是数据更新时触发的

一、初始化的时候:

1》数据代理

数据代理就是通过一个对象代理对另一个对象中属性的操作(读/写);vue中通过vm代理data对象(vm代理vm._data)中所有属性的操作,更方便的操作data中的数据

vue中数据代理:将vue文件中的data保存一份到vm._data; 然后对将vue文件中data中的每个属性添加到vm实例上,通过Object.defineProperty实现数据的代理;当我们读取vm上的属性时,他会到vm._data中找到对应的属性返回当,我们修改vue实例对象的属性后,对应的setter就会监听到变化,然后去修改实例对象上vm._data对应的属性

    Object.defineProperty(me, key, {
      configurable: false, // 不可以重新定义
      enumerable: true, // 可枚举遍历
      // 当执行vm.name获取属性值时自动调用返回属性值
      get: function proxyGetter() {
        // 读取data中对应的属性值返回
        return me._data[key];
      },
      // 当执行vm.name = "xxx"时自动调用
      set: function proxySetter(newVal) {
        // 将最新的值保存给data对应的属性上
        me._data[key] = newVal;
      }
    });

2》数据绑定:

数据绑定

初始化显示:页面(表达式、指令)能从data读取数据希纳是(编译、解析)

更新显示:更新data中的属性数据,能够更新页面

数据劫持

1》数据劫持是vue实现数据绑定的一种技术

2》基本思想:通过defineProperty()来监视data中所有属性(任意层次)数据的变化,一旦变化就更新界面

具体实现:

采用递归的方式为data中的每个层级的属性创建dep(实例对象),并通过defineProperty对data进行重新定义,实现数据劫持;在set中判断数据是否发生了变化,如果发生改变,一方面他会更新值,新的值是需要重新劫持监听;另一方面会通知所有相关订阅者watcher去更新界面;在get中不仅返回值,还需要建立watcher与dep的关系(这个get会在模板解析大括号表达式和指令时触发);给Watcher添加Dep,给watch的subs中push对应dep

dep对象:

{
id;0,
subs:[]
}

dep的id从0开始递增,每个属性对应一个dep,劫持

watcher对象:

{
  vm:MVVM
  exp:"name",
  depIds:{depId:dep}  // depId就是上面dep的id,是个数字
  cb: textUpdater,
  value:"luca"
}

watcher与dep的关系

多对多的关系

一个dep可能对应多个watcher; eg:一个属性在多个标签中使用

一个watcher可能的对应多个dep; eg: {{a.b.c}}

什么时候建立关系:编译、解析模板(大括号表达式和非事件指令)时建立

怎么建立关系:创建watcher时会读取data中的值,defineProperty中get会建立双方关系;在dep的subs中push了watcher,且在watcher的depIds中添加了对应dep

劫持的部分代码:

  defineReactive: function (data, key, val) {
    // 创建对应的dep对象
    var dep = new Dep();
    // 通过隐式递归调用, 实现对所有层次属性的劫持
    var childObj = observe(val);
    // 给data中指定属性进行重新定义
    Object.defineProperty(data, key, {
      enumerable: true, // 可枚举
      configurable: false, // 不能再define
      // 返回属性值, 同建立dep与watcher之间的关系
      get: function () {
        if (Dep.target) {
          dep.depend();
        }
        return val;
      },
      // 监视属性值的变化, 一旦变化去更新对应的界面
      set: function (newVal) {
        if (newVal === val) {
          return;
        }
        val = newVal;
        // 是object的新的值话,进行监听
        childObj = observe(newVal);
        // 通知订阅者
        dep.notify();
      },
   });
  },

3》编译模板

1、将el所有的子节点去除,添加到加到一个新建的文档 fragment 对象中

2、对 fragment中所有层次子节点递归进行编译解析处理

编译大括号表达式和一般指令时创建watcher;创建watcher时指定了更新函数(因为要更新的可能是a.b.c这种,所以要遍历,取值是从_data中取的);这是初始化需要读取_data中的数据,就会走对应属性的get,从而建立dep与watcher的关系,给dep的subs中push了对应的watcher,并且在watcher中的depIds添加了对应的dep

1》对表达式文本进行解析

​ a、根据正则对象匹配的表达式字符串( 匹配eg:{{obj.age}} ):子匹配

​ b、将属性值设置为文本节点的 textContent

​ c、从data中去除表达式对应的属性(通过.来分割成数组后遍历,因为可能由多层 a.b.c)

2》对元素节点的指令属性进行解析

a》 事件指令的解析

​ a.1、从指令中取出事件名

​ a2、根据指令的值从methods中得到对应的事件处理函数对象

​ a3、给当前元素节点绑定事件名和事件回调函数的事件监听

​ a4、指令解析完成后,移除次指令属性

b》一般指令的解析

​ b.1、从表达式中取出指令名和指令值

​ b.2、从data中根据表达式得到对应的值

​ b.3、根据指令名确定需要操作的元素节点的什么属性

​ b.4 、将表达式的值设置到对应的属性上(v-text:textContent;;v-html:innerHtml; v-class :className)

​ b5、移除元素的指令属性

3、将解析后的 fragment 添加到el中显示

fragment修改并不会引起页面的更新

二、数据变化的时候

监听到_data的变化,set首先判断值有没有变化,发生变化而且是个对象的话会重新调用observe函数进行监听;通知所有相关的订阅者更新界面(通过遍历subs,调用watcher中的回调函数函数来更新界面)

双向数据绑定的实现

双向绑定是建立在单项数据绑定的基础上;只是在解析v-model指令时,给当前元素添加了input监听;当input的value发生改变时,修改对应的_data中的属性

v-model 是动态属性绑定 v-bind 与 input事件的语法糖

关的订阅者更新界面(通过遍历subs,调用watcher中的回调函数函数来更新界面)

双向数据绑定的实现

双向绑定是建立在单项数据绑定的基础上;只是在解析v-model指令时,给当前元素添加了input监听;当input的value发生改变时,修改对应的_data中的属性

v-model 是动态属性绑定 v-bind 与 input事件的语法糖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值