渲染render function之后就是 核心的响应式过程了

Object.defineProperty

vue的核心之一就是Object.defineProperty 方法(IE9及其以上)

  1. Object.defineProperty(obj, prop, descriptor) //可以定义新的属性或者修改原来的属性
    参数说明
      obj:必需,目标对象; prop:必需,需要定义或者修改的属性名;descriptor:添加对要操作属性的描述
  1. var obj = {};
  2. Object.defineProperty(obj, 'msg', {
  3. get () {
  4. console.log('get');
  5. },
  6. set (newVal) {
  7. console.log('set', newVal);
  8. }
  9. });
  10. obj.msg // get
  11. obj.msg = 'hello world' // set hello world

取 obj 对象中 msg 的值时会调用 get 方法,给 msg 赋值时会调用 set 方法,并接收新值作为其参数。

这里提一句,在 Vue 中我们调用数据是直接 this.xxx ,而数据其实是 this.data.xxx,原来 Vue 在初始化数据的时候会遍历 data 并代理这些数据。

  1.  
  1. Object.keys(this.data).forEach((key) => {
  2. this.proxyKeys(key);
  3. });
  4.  
  5. proxyKeys (key) {
  6. Object.defineProperty(this, key, {
  7. enumerable: false,
  8. configurable: true,
  9. get() {
  10. return this.data[key];
  11. },
  12. set(newVal) {
  13. this.data[key] = newVal;
  14. }
  15. });
  16. }
  1.  

上面可以看到,取 this.key 的值其实是取 this.data.key 的值,赋值同理。

现在,我们已经知道如何去检测数据的变化,并且做出一些响应了。

观察者模式(发布者-订阅者模式)

vue 的响应式系统依赖于三个重要的类:Dep 类、Watcher 类、Observer 类。

Dep 类作为发布者的角色,Watcher 类作为订阅者的角色,Observer 类则是连接发布者和订阅者的纽带,决定订阅和发布的时机。

我们先看下面的代码,来对发布者和订阅者有个初步的了解。

  1. class Dep {
  2. constructor() {
  3. this.subs = [];
  4. }
  5.  
  6. addSub(watcher) {
  7. this.subs.push(watcher);
  8. }
  9.  
  10. notify() {
  11. this.subs.forEach(watcher => {
  12. watcher.update();
  13. });
  14. }
  15. }
  16.  
  17. class Watcher {
  18. constructor() {
  19. }
  20.  
  21. update() {
  22. // 接收通知后的处理方法
  23. }
  24. }
  25.  
  26. const dep = new Dep(); // 发布者 dep
  27. const watcher1 = new Watcher(); // 订阅者1 watcher1
  28. const watcher2 = new Watcher(); // 订阅者2 watcher2
  29. dep.addSub(watcher1); // watcher1 订阅 dep
  30. dep.addSub(watcher2); // watcher2 订阅 dep
  31. dep.notify(); // dep 发送通知

面我们定义了一个发布者 dep,两个订阅者 watcher1、watcher2。让 watcher1、watcher2 都订阅 dep,当 dep 发送通知时,watcher1、watcher2 都能做出各自的响应。

现在我们已经了解了发布者和订阅者的关系,那么剩下的就是订阅和发布的时机。什么时候订阅?什么时候发布?想到上面提到的 Object.defineProperty ,想必你已经有了答案。

我们来看 Observer 类的实现:

  1. class Observer {
  2. constructor(data) {
  3. this.data = data;
  4. this.walk();
  5. }
  6.  
  7. walk() {
  8. Object.keys(this.data).forEach(key => {
  9. this.defineReactive(this.data, key, this.data[key]);
  10. });
  11. }
  12.  
  13. defineReactive(data, key, value) {
  14. const dep = new Dep();
  15.  
  16. if ( value && typeof value === 'object' ) {
  17. new Observer(value);
  18. }
  19.  
  20. Object.defineProperty(data, key, {
  21. enumerable: true,
  22. configurable: true,
  23. get() {
  24. if (Dep.target) {
  25. dep.addSub(Dep.target); // 订阅者订阅 Dep.target 即当前 Watcher 类的实例(订阅者)
  26. }
  27. return value;
  28. },
  29. set(newVal) {
  30. if (newVal === value) {
  31. return false;
  32. }
  33. value = newVal;
  34. dep.notify(); // 发布者发送通知
  35. }
  36. });
  37. }
  38. }

在 Observer 类中,为 data 的每个属性都实例化一个 Dep 类,即发布者。并且在取值时让订阅者(有多个,因为 data 中的每个属性都可以被应用在多个地方)订阅,在赋值时发布者发布通知,让订阅者做出各自的响应。

这里需要提的是 Dep.target,这其实是 Watcher 类的实例,我们可以看看 Watcher 的详细代码:

  1. class Watcher {
  2. constructor(vm, exp, cb) {
  3. this.vm = vm;
  4. this.exp = exp; // data 属性名
  5. this.cb = cb; // 回调函数
  6.  
  7. // 将自己添加到订阅器
  8. this.value = this.getValue();
  9. }
  10.  
  11. update() {
  12. const value = this.vm.data[this.exp];
  13. const oldValue = this.value;
  14. if (value !== oldValue) {
  15. this.value = value;
  16. this.cb.call(this.vm, value, oldValue); // 执行回调函数
  17. }
  18. }
  19.  
  20. getValue() {
  21. Dep.target = this; // 将自己赋值给 Dep.target
  22. const value = this.vm.data[this.exp]; // 取值操作触发订阅者订阅
  23. Dep.target = null;
  24. return value;
  25. }
  26. }

Watcher 类在构造函数中执行了一个 getValue 方法,将自己赋值给 Dep.target ,并且执行了取值操作,这样就成功的完成了订阅操作。一旦数据发生变化,即有了赋值操作,发布者就会发送通知,订阅者就会执行自己的 update 方法来响应这次数据变化

数据的双向绑定

数据的双向绑定即数据和视图之间的同步,视图随着数据变化而变化,反之亦然。我们知道 Vue 是支持数据的双向绑定的,主要应用于表单,是通过 v-model 指令来实现的。而通过上面介绍的知识我们是可以知道如何实现视图随着数据变化的,那么如何让数据也随着视图变化而变化呢?其实也很简单,只要给有 v-model 指令的节点监听相应的事件即可,在事件回调中来改变相应的数据。这一切都 Compile 类中完成,假设有一个 input 标签应用了 v-model 指令,在开始编译模板时,遇到 v-model 指令时会执行:更新 dom 节点的值,订阅者订阅,事件监听。

  1. compileModel (node, vm, exp) {
  2. let val = vm[exp];
  3.  
  4. // 更新内容
  5. this.modelUpdater(node, val);
  6.  
  7. // 添加订阅
  8. new Watcher(vm, exp, (value) => {
  9. // 数据改变时的回调函数
  10. this.modelUpdater(node, value);
  11. });
  12.  
  13. // 事件监听
  14. node.addEventListener('input', (e) => {
  15. const newValue = e.target.value;
  16. if (val === newValue) {
  17. return false;
  18. }
  19. vm[exp] = newValue;
  20. val = newValue;
  21. });
  22. }

Vue的响应原理的更多相关文章

  1. 仿VUE创建响应式数据

    VUE对于前端开发人员都非常熟悉了,其工作原理估计也都能说的清个大概,具体代码的实现估计看的人不会太多,这里对vue响应式数据做个简单的实现. 先简单介绍一下VUE数据响应原理,VUE响应数据分为对象 ...

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

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

  3. vue深入响应式原理

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

  4. Vue 数据响应式原理

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

  5. 深入解析vue.js响应式原理与实现

    vue.js响应式原理解析与实现.angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面.vue.js ...

  6. Vue的响应式原理

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

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

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

  8. vue系列---响应式原理实现及Observer源码解析(一)

    _ 阅读目录 一. 什么是响应式? 二:如何侦测数据的变化? 2.1 Object.defineProperty() 侦测对象属性值变化 2.2 如何侦测数组的索引值的变化 2.3 如何监听数组内容的 ...

  9. 由自定义事件到vue数据响应

    前言 除了大家经常提到的自定义事件之外,浏览器本身也支持我们自定义事件,我们常说的自定义事件一般用于项目中的一些通知机制.最近正好看到了这部分,就一起看了下自定义事件不同的实现,以及vue数据响应的基 ...

随机推荐

  1. form表单加密前台js后台java

    1.前台javascript 1.在提交的js中这样写 <!-- 加密解密 --> document.form1.username.value=encode64(document.form ...

  2. AppManager类,管理Activity和App

    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); / ...

  3. NOIP 2012 疫情控制(二分+贪心+倍增)

    题解 二分时间 然后一个显然的事是一个军队向上爬的越高它控制的点越多 所以首先军队尽量往上爬. 当一个军队可以爬到根节点我们记录下它的剩余时间T和它到达根结点时经过的根节点的子节点son. 当一个军队 ...

  4. Zookeeper入门:基本概念、5项配置、启动

    起源 最早接触Zookeeper,是在学习Hadoop权威指南这本书的时候,印象中是Hadoop项目的一个子工程.      最近,项目中需要用到"分布式锁".      之前,在 ...

  5. [Recompose] Compose Streams of React Props with Recompose’s compose and RxJS

    Functions created with mapPropsStream canned be composed together to build up powerful streams. Brin ...

  6. MFC Wizard创建的空应用程序中各个文件内容的解析

    创建的MFC应用程序名为:wd,那么: 一.wd.h解析 // wd.h : main header file for the WD application // #if !defined(AFX_W ...

  7. Entity Framework介绍和DBFirst开发方式

    一.ORM概念  什么是ORM? 对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术.简单来说,就是将关系型数 ...

  8. Long和long判断

    Long需要equals 判断. long可以==

  9. FTP配置说明

    1.下载rpm包,如vsftpd-3.0.2-21.el7.x86_64.rpm.可在系统盘里面找到或者下载 2.参考如下步骤.或者见链接http://blog.csdn.net/uq_jin/art ...

  10. 25.Detours劫持技术

    Detours可以用来实现劫持,他是微软亚洲研究院开发出来的工具,要实现它首先需要安装Detours. 安装地址链接:https://pan.baidu.com/s/1eTolVZs 密码:uy8x ...