vue.js响应式原理解析与实现。angularjs是通过脏检查来实现数据监测以及页面更新渲染。之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面。vue.js响应式原理解析与实现
  
  Object.defineProperty
  
  es5新增了Object.defineProperty这个api,它可以允许我们为对象的属性来设定getter和setter,从而我们可以劫持用户对对象属性的取值和赋值。比如以下代码:
  
  const obj = {
  
  };
  
  let val = 'cjg';
  
  Object.defineProperty(obj, 'name', {
  
  get() {
  
  console.log('劫持了你的取值操作啦');
  
  return val;
  
  },
  
  set(newVal) {
  
  console.log('劫持了你的赋值操作啦');
  
  val = newVal;
  
  }
  
  });
  
  console.log(obj.name);
  
  obj.name = 'cwc';
  
  console.log(obj.name);
  
  //欢迎加入全栈开发交流圈一起学习交流:864305860
  
  我们通过Object.defineProperty劫持了obj[name]的取值和赋值操作,我们可以在obj[name]被赋值的时候触发更新页面操作。
  
  发布订阅模式
  
  当事件发生的时候,发布者通知所有订阅该事件的订阅者。我们来看一个例子了解下。
  
  class Dep {
  
  constructor() {
  
  this.subs = [];
  
  }
  
  // 增加订阅者
  
  addSub(sub) {
  
  if (this.subs.indexOf(sub) < 0) {
  
  this.subs.push(sub);
  
  }
  
  }
  
  // 通知订阅者
  
  notify() {
  
  this.subs.forEach((sub) => {
  
  sub.update();
  
  })
  
  }
  
  }
  
  const dep = new Dep();
  
  const sub = {
  
  update() {
  
  console.log('sub1 update')
  
  }
  
  }
  
  const sub1 = {
  
  update() {
  
  console.log('sub2 update');
  
  }
  
  }
  
  dep.addSub(sub);
  
  dep.addSub(sub1);
  
  dep.notify(); // 通知订阅者事件发生,触发他们的更新函数
  
  全栈开发交流圈:864305860.png
  
  vue.js首先通过Object.defineProperty来对要监听的数据进行getter和setter劫持,当数据的属性被赋值/取值的时候,vue.js就可以察觉到并做相应的处理。
  
  class Observer {
  
  constructor(data) {
  
  // 如果不是对象,则返回
  
  if (!data || typeof data !== 'object') {
  
  return;
  
  }
  
  this.data = data;
  
  this.walk();
  
  }
  
  // 对传入的数据进行数据劫持
  
  walk() {
  
  for (let key in this.data) {
  
  this.defineReactive(this.data, key, this.data[key]);
  
  }
  
  }
  
  // 创建当前属性的一个发布实例,使用Object.defineProperty来对当前属性进行数据劫持。
  
  defineReactive(obj, key, val) {
  
  // 创建当前属性的发布者
  
  const dep = new Dep();
  
  /*
  
  * 递归对子属性的值进行数据劫持,比如说对以下数据
  
  * let data = {
  
  * name: 'cjg',
  
  * obj: {
  
  * name: 'zht',
  
  * age: 22,
  
  * obj: {
  
  * name: 'cjg',
  
  * age: 22,
  
  * }
  
  * },
  
  * };
  
  * 我们先对data最外层的name和obj进行数据劫持,之后再对obj对象的子属性obj.name,obj.age, obj.obj进行数据劫持,层层递归下去,直到所有的数据都完成了数据劫持工作。
  
  */
  
  new Observer(val);
  
  Object.defineProperty(obj, key, {
  
  get() {
  
  // 若当前有对该属性的依赖项,则将其加入到发布者的订阅者队列里
  
  if (Dep.target) {
  
  dep.addSub(Dep.target);
  
  }
  
  return val;
  
  },
  
  set(newVal) {
  
  if (val === newVal) {
  
  return;
  
  }
  
  val = newVal;
  
  new Observer(newVal);
  
  dep.notify();
  
  }
  
  })
  
  }
  
  }
  
  // 发布者,将依赖该属性的watcher都加入subs数组,当该属性改变的时候,则调用所有依赖该属性的watcher的更新函数,触发更新。
  
  class Dep {
  
  constructor() {
  
  this.subs = [];
  
  }
  
  addSub(sub) {
  
  if (this.subs.indexOf(sub) < 0) {
  
  this.subs.push(sub);
  
  }
  
  }
  
  notify() {
  
  this.subs.forEach((sub) => {
  
  sub.update();
  
  })
  
  }
  
  }
  
  Dep.target = null;
  
  // 观察者
  
  class Watcher {
  
  /**
  
  *Creates an instance of Watcher.
  
  * @param {*} vm
  
  * @param {*} keys
  
  * @param {*} updateCb
  
  * @memberof Watcher
  
  */
  
  constructor(vm, keys, updateCb) {
  
  this.vm = vm;
  
  this.keys = keys;
  
  this.updateCb = updateCb;
  
  this.value = null;
  
  this.get();
  
  }
  
  // 根据vm和keys获取到最新的观察值
  
  get() {
  
  Dep.target = this;
  
  const keys = this.keys.split('.');
  
  let value = this.vm;
  
  keys.forEach(_key => {
  
  value = value[_key];
  
  });
  
  this.value = value;
  
  Dep.target www.michenggw.com= null;
  
  return this.value;
  
  }//欢迎加入全栈开发交流圈一起学习交流:864305860
  
  update() {
  
  const oldValue = this.value;
  
  const newValue = this.get();
  
  if (oldValue !== newValue) {
  
  this.updateCb(oldValue, newValue);
  
  }
  
  }
  
  }
  
  let data = {
  
  name: 'cjg',
  
  obj: {
  
  name: 'zht',
  
  },
  
  };
  
  new Observer(data);
  
  // 监听data对象的name属性,当data.name发现变化的时候,触发cb函数
  
  new Watcher(data, 'name', www.gcyl152.com/(oldValue, newValue) => {
  
  console.log(oldValue, newValue);
  
  })
  
  data.name = 'zht';
  
  // 监听data对象的obj.name属性,当data.obj.name发现变化的时候,触发cb函数
  
  new Watcher(data, 'obj.name',www.gcyL157.com (oldValue, www.dfgjpt.com newValue) => {
  
  console.log(oldValue,www.mingcheng178.com newValue);
  
  })
  
  data.obj.name =www.furggw.com 'cwc';
  
  data.obj.name =www.mhylpt.com 'dmh';
  
  这样,一个简单的响应式数据监听就完成了。当然,这个也只是一个简单的demo,来说明vue.js响应式的原理,真实的vue.js源码会更加复杂,因为加了很多其他逻辑。

深入解析vue.js响应式原理与实现的更多相关文章

  1. vue.js响应式原理解析与实现

    vue.js响应式原理解析与实现 从很久之前就已经接触过了angularjs了,当时就已经了解到,angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很 ...

  2. Vue.js响应式原理

      写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出. 文章的原地址:answershuto/learnV ...

  3. vue.js响应式原理解析与实现—实现v-model与{{}}指令

    上一节我们已经分析了vue.js是通过Object.defineProperty以及发布订阅模式来进行数据劫持和监听,并且实现了一个简单的demo.今天,我们就基于上一节的代码,来实现一个MVVM类, ...

  4. Vue.js 响应式原理

    1. Vue2.x 基于 Object.defineProperty 方法实现响应式(Vue3 将采用 Proxy) Object.defineProperty(obj, prop, descript ...

  5. Vue 源码解析:深入响应式原理(上)

    原文链接:http://www.imooc.com/article/14466 Vue.js 最显著的功能就是响应式系统,它是一个典型的 MVVM 框架,模型(Model)只是普通的 JavaScri ...

  6. vue深入响应式原理

    vue深入响应式原理 深入响应式原理 — Vue.jshttps://cn.vuejs.org/v2/guide/reactivity.html 注意:这里说的响应式不是bootsharp那种前端UI ...

  7. Vue 数据响应式原理

    Vue 数据响应式原理 Vue.js 的核心包括一套“响应式系统”.“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码.例如,视图渲染中使用了数据,数据改变后,视图也会自动更新. 举个简单 ...

  8. Vue的响应式原理

    Vue的响应式原理 一.响应式的底层实现 1.Vue与MVVM Vue是一个 MVVM框架,其各层的对应关系如下 View层:在Vue中是绑定dom对象的HTML ViewModel层:在Vue中是实 ...

  9. 一探 Vue 数据响应式原理

    一探 Vue 数据响应式原理 本文写于 2020 年 8 月 5 日 相信在很多新人第一次使用 Vue 这种框架的时候,就会被其修改数据便自动更新视图的操作所震撼. Vue 的文档中也这么写道: Vu ...

随机推荐

  1. Oracle口令文件管理

    Oracle的口令文件目录 $ORACLE_HOME/dbs/orapw$ORACLE_SID 建立口令文件 orapwd file=$ORACLE_HOME/dba/orapw$ORACLE_SID ...

  2. poj_3641_Pseudoprime numbers

    Fermat's theorem states that for any prime number p and for any integer a > 1, ap = a (mod p). Th ...

  3. MySQL如何计算重要的指标,来确定配置是否正确

    在调优MySQL数据库和监控数据库时,很多朋友不知道如何下手,怎么来确定是不是参数设置的合理,下面给出一些如何计算指标,来确定数据库参数是否设置合理,希望给大家一些方法,去做MySQL数据库优化,最好 ...

  4. Salt-ssh 自动安装salt-minion

    作用:为了不手动去安装一台一台去salt-minion,并进重复的配置 一.环境 系统环境: #cat /etc/redhat-release CentOS Linux release 7.4.170 ...

  5. 使用Docker 一键部署 LNMP+Redis 环境

    使用Docker 部署 LNMP+Redis 环境 Docker 简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linu ...

  6. Shell学习——Shell分类:登录shell和非登陆shell 交互shell和非交互shell

    1.从两个不同维度来划分,是否交互式,是否登录 2.交互式shell和非交互式shell 交互式模式:在终端上执行,shell等待你的输入,并且立即执行你提交的命令.这种模式被称作交互式是因为shel ...

  7. static作用域

    当一个函数完成时,它的所有变量通常都会被删除.然而,有时候您希望某个局部变量不要被删除. 要做到这一点,请在您第一次声明变量时使用 static 关键字: <?php function myTe ...

  8. 20145202 《Java程序设计》第四周学习总结

    继承:打破了封装性 extends 1.提高了代码的复用性. 2.让类与类之间产生了关系,有了这个关系,才有了多态的特性. 3.必须是类与类之间有所属类关系才可以继承. 4.java只支持单继承不支持 ...

  9. PHP.21-商品信息管理

    商品信息管理 在线增删改查和图片信息管理 主要技术:文件上传.图片缩放.数据库基本操作 思路: 1.设计并创建数据库 库名:demodb 表名:goods 编号(id) 名称(name) 商品类型(t ...

  10. SetConsoleCtrlHandler

    Excerpt: Registering a Control Handler Function   This is an example of the SetConsoleCtrlHandler fu ...