Vue Object 的变化侦测实现代码


Posted in Javascript onApril 15, 2020

数据观察

Vue 中的对象变化侦测是通过Object.definePorperty实现的,但是Object.definePorperty的方式有缺陷,比如不能直接代理整个对象,每次都要循环遍历对象的所有属性;尤大大说之后会使用 ES6 中的Proxy 重写这个部分。这篇博客介绍的是 Object.definePorperty 实现的对象侦测。

我们来看下面这段代码,定义一个 defineReactive 函数,使用 Object.definePorperty 遍历对象对象属性的时候,设置 get 和 set;当对象属性被读取的时候触发 get,对象属性被设置的时候触发 set。这样就完成了对 data 的数据劫持,因为 Vue 的思想是响应式的,我们还需要收集这些变化。

function defineReactive(data,key,val){
  Object.definePorperty(data,key,{
    enumerable: true,
    configurable: true,
    get:function(){
      return val;
    }
    set :function (newVal){
      if(val === newVal){return}
      val = newVal;
    }
  })
}

依赖 收集

创建一个 Dep 类,在 get 中收集依赖,在 set 中新增依赖

class Dep{
  constructor(){
    this.arr = []
  }
  addSub(sub){
    this.arr.push(sub)
  }
   removeSub(sub){
    remove(this.arr,sub)
  }
  depend(){
    if(window.target){
      this.addSub(window.target)
    }
  }
  notify(){
    const arrs = this.arr.slice();
    for(let i = 0; i< arrs.lenth ;i ++){
      arrs[i].update();
    }
  }

}

function defineReactive(data,key,val){

  let dep = new Dep()
  Object.definePorperty(data,key,{
    enumerable: true,
    configurable: true,
    get:function(){
      dep.depend(); // 收集依赖
      return val;
    }
    set :function (newVal){
      if(val === newVal){return}
      val = newVal;
      dep.notify(); // 新增依赖
    }
  })
}

Observer 和 Watcher

我们发现 defineReactive 函数只能将某一个属性转换为 get/set 的形式,所以我们需要一个观察者 Observer 用来帮助递归的侦测所有的 key

class Observer{
  constructor(value){
    this.value = value
  }
  if(!Array.isArry(value)){
    this.walk(value)
  }
  walk(obj){
    const keys = Object.keys(obj)
    for(let i = 0; i < keys.length ;i++){
      defineReactive(data,keys[i],obj[keys[i])
    }
  }
}

当这些依赖收集完成之后,我们要通知谁呢?怎么样能让视图知道有变化更新?我们需要实现一个订阅者 Watcher,
每次触发 get 的时候都将 dep 指向自己,这样就可以收集到依赖;
每次 set 的时候都循环调用 Watcher 的 update 方法。

class Watcher{
  constructor(vm,exp,cb){
    this.vm = vm;
    this.cb = cb;
    this.exp = exp;
    this.value = this.get();
  }
  get(){
    Dep.target = this;  // 将当前订阅者指向自己
    var value = this.vm[exp];  // 触发getter,添加自己到属性订阅器中
    Dep.target = null;  // 添加完毕,重置
    return value;
  }
  update(){
    const oldVal = this.value;
    this.value = this.get();
    this.cb.call(this.vm,this.value,oldVal)
  }
}

当 Vue 实例挂载好之后,模板都会绑定一个 Watcher,谁的属性发生变化了就会通知响应的 Watcher,Watcher 再去通知编译器 Compile 进行视图更新

侦测没办法监听到对象上属性的新增和删除

Vue 通过Object.definePorperty将对象的 key 转化为 getter setter 的形式来进行侦测,但是无法追踪到属性的新增和删除,所以 Vue 中提供了 vm.get 来实现

到此这篇关于Vue Object 的变化侦测实现代码的文章就介绍到这了,更多相关Vue Object 变化侦测 内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
JavaScript Cookie的读取和写入函数
Dec 08 Javascript
js报错 Object doesn't support this property or method的原因分析
Mar 31 Javascript
iframe的父子窗口之间的对象相互调用基本用法
Sep 03 Javascript
JavaScript性能优化之小知识总结
Nov 20 Javascript
javascript基于原型链的继承及call和apply函数用法分析
Dec 15 Javascript
如何提高数据访问速度
Dec 26 Javascript
Canvas 绘制粒子动画背景
Feb 15 Javascript
简单谈谈require模块化jquery和angular的问题
Jun 23 jQuery
BootStrap导航栏问题记录
Jul 31 Javascript
微信小程序配置服务器提示验证token失败的解决方法
Apr 03 Javascript
JS实现简易留言板特效
Dec 23 Javascript
浅谈vant组件Picker 选择器选单选问题
Nov 04 Javascript
Vue项目vscode 安装eslint插件的方法(代码自动修复)
Apr 15 #Javascript
小程序按钮避免多次调用接口和点击方案实现(不用showLoading)
Apr 15 #Javascript
javascript设计模式 ? 享元模式原理与用法实例分析
Apr 15 #Javascript
javascript设计模式 ? 外观模式原理与用法实例分析
Apr 15 #Javascript
写给新手同学的vuex快速上手指北小结
Apr 14 #Javascript
vue-cli设置publicPath小记
Apr 14 #Javascript
vue 实现用户登录方式的切换功能
Apr 14 #Javascript
You might like
php数组函数序列之array_unshift() 在数组开头插入一个或多个元素
2011/11/07 PHP
如何使用php判断所处服务器操作系统的类型
2013/06/20 PHP
PHP数组和explode函数示例总结
2015/05/08 PHP
php基于jquery的ajax技术传递json数据简单实例
2016/04/15 PHP
PHP面向对象程序设计之类与反射API详解
2016/12/02 PHP
js控制表单操作的常用代码小结
2013/08/15 Javascript
jQuery之过滤元素操作小结
2013/11/30 Javascript
jquery点击缩略图切换视频播放特效代码分享
2015/09/15 Javascript
jQuery中的ready函数与window.onload谁先执行
2016/06/21 Javascript
jQuery向webApi提交post json数据
2017/01/16 Javascript
JavaScript使用原型和原型链实现对象继承的方法详解
2017/04/05 Javascript
JS实现点击Radio动态更新table数据
2017/07/18 Javascript
Angular4.0中引入laydate.js日期插件的方法教程
2017/12/25 Javascript
Vue下滚动到页面底部无限加载数据的示例代码
2018/04/22 Javascript
js校验开始时间和结束时间
2020/05/26 Javascript
vue移动端写的拖拽功能示例代码
2020/09/09 Javascript
vue大型项目之分模块运行/打包的实现
2020/09/21 Javascript
[50:58]2018DOTA2亚洲邀请赛 4.1 小组赛 B组 Mineski vs EG
2018/04/03 DOTA
[49:29]LGD vs Winstrike 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
如何在python字符串中输入纯粹的{}
2018/08/22 Python
python pandas模块基础学习详解
2019/07/03 Python
pytorch 移动端部署之helloworld的使用
2020/10/30 Python
pycharm 关闭search everywhere的解决操作
2021/01/15 Python
python如何发送带有附件、正文为HTML的邮件
2021/02/27 Python
Python实现图片指定位置加图片水印(附Pyinstaller打包exe)
2021/03/04 Python
CSS3实现多重边框的方法总结
2016/05/31 HTML / CSS
详解CSS3的图层阴影和文字阴影效果使用
2016/06/09 HTML / CSS
Html5之自定义属性(data-,dataset)
2019/11/19 HTML / CSS
中秋节超市促销方案
2014/01/30 职场文书
项目申报专员岗位职责
2014/07/09 职场文书
志愿者爱心公益活动策划方案
2014/09/15 职场文书
个人股份合作协议书
2014/10/24 职场文书
先进个人材料怎么写
2014/12/30 职场文书
Mysql 设置boolean类型的操作
2021/06/04 MySQL
Pytest中skip skipif跳过用例详解
2021/06/30 Python
Python实现查询剪贴板自动匹配信息的思路详解
2021/07/09 Python