交流群:462197261站长百科站长论坛热门标签收藏本站北冥有鱼 互联网前沿资源第一站 助力全行业互联网+
点击这里给我发消息
  • 当前位置:
  • Vue响应式原理Observer、Dep、Watcher理解

    开篇

    最近在学习Vue的源码,看了网上一些大神的博客,看起来感觉还是蛮吃力的。自己记录一下学习的理解,希望能够达到简单易懂,不看源码也能理解的效果😆

    Object.defineProperty

    相信很多同学或多或少都了解Vue的响应式原理是通过Object.defineProperty实现的。被Object.defineProperty绑定过的对象,会变成「响应式」化。也就是改变这个对象的时候会触发get和set事件。进而触发一些视图更新。举个栗子🌰

    function defineReactive (obj, key, val) {
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: () => {
          console.log('我被读了,我要不要做点什么好?');
          return val;
        },
        set: newVal => {
          if (val === newVal) {
            return;
          }
          val = newVal;
          console.log("数据被改变了,我要把新的值渲染到页面上去!");
        }
      })
    }
    
    let data = {
      text: 'hello world',
    };
    
    // 对data上的text属性进行绑定
    defineReactive(data, 'text', data.text);
    
    console.log(data.text); // 控制台输出 <我被读了,我要不要做点什么好?>
    data.text = 'hello Vue'; // 控制台输出 <hello Vue && 数据被改变了,我要把新的值渲染到页面上去!>
    
    

    Observer 「响应式」

    Vue中用Observer类来管理上述响应式化Object.defineProperty的过程。我们可以用如下代码来描述,将this.data也就是我们在Vue代码中定义的data属性全部进行「响应式」绑定。

    class Observer {
      constructor() {
        // 响应式绑定数据通过方法
       observe(this.data);
      }
    }
    
    export function observe (data) {
      const keys = Object.keys(data);
      for (let i = 0; i < keys.length; i++) {
        // 将data中我们定义的每个属性进行响应式绑定
        defineReactive(obj, keys[i]);
      }
    }
    
    

    Dep 「依赖管理」

    什么是依赖?

    相信没有看过源码或者刚接触Dep这个词的同学都会比较懵。那Dep究竟是用来做什么的呢? 我们通过defineReactive方法将data中的数据进行响应式后,虽然可以监听到数据的变化了,那我们怎么处理通知视图就更新呢?

    Dep就是帮我们收集【究竟要通知到哪里的】。比如下面的代码案例,我们发现,虽然data中有text和message属性,但是只有message被渲染到页面上,至于text无论怎么变化都影响不到视图的展示,因此我们仅仅对message进行收集即可,可以避免一些无用的工作。

    那这个时候message的Dep就收集到了一个依赖,这个依赖就是用来管理data中message变化的。

    <div>
      <p>{{message}}</p>
    </div>
    
    data: {
      text: 'hello world',
      message: 'hello vue',
    }
    

    当使用watch属性时,也就是开发者自定义的监听某个data中属性的变化。比如监听message的变化,message变化时我们就要通知到watch这个钩子,让它去执行回调函数。

    这个时候message的Dep就收集到了两个依赖,第二个依赖就是用来管理watch中message变化的。

    watch: {
      message: function (val, oldVal) {
        console.log('new: %s, old: %s', val, oldVal)
      },
    }    
    

    当开发者自定义computed计算属性时,如下messageT属性,是依赖message的变化的。因此message变化时我们也要通知到computed,让它去执行回调函数。 这个时候message的Dep就收集到了三个依赖,这个依赖就是用来管理computed中message变化的。

    computed: {
      messageT() {
        return this.message + '!';
      }
    }
    

    图示如下:一个属性可能有多个依赖,每个响应式数据都有一个Dep来管理它的依赖。

    如何收集依赖

    我们如何知道data中的某个属性被使用了,答案就是Object.defineProperty,因为读取某个属性就会触发get方法。可以将代码进行如下改造:

    function defineReactive (obj, key, val) {
      let Dep; // 依赖
    
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: () => {
          console.log('我被读了,我要不要做点什么好?');
          // 被读取了,将这个依赖收集起来
          Dep.depend(); // 本次新增
          return val;
        },
        set: newVal => {
          if (val === newVal) {
            return;
          }
          val = newVal;
          // 被改变了,通知依赖去更新
          Dep.notify(); // 本次新增
          console.log("数据被改变了,我要把新的值渲染到页面上去!");
        }
      })
    }
    
    

    什么是依赖

    那所谓的依赖究竟是什么呢?上面的图中已经暴露了答案,就是Watcher。

    Watcher 「中介」

    Watcher就是类似中介的角色,比如message就有三个中介,当message变化,就通知这三个中介,他们就去执行各自需要做的变化。

    Watcher能够控制自己属于哪个,是data中的属性的还是watch,或者是computed,Watcher自己有统一的更新入口,只要你通知它,就会执行对应的更新方法。

    因此我们可以推测出,Watcher必须要有的2个方法。一个就是通知变化,另一个就是被收集起来到Dep中去。

    class Watcher {
      addDep() {
        // 我这个Watcher要被塞到Dep里去了~~
      },
      update() {
        // Dep通知我更新呢~~
      }, 
    }
    

    总结

    回顾一下,Vue响应式原理的核心就是Observer、Dep、Watcher。

    Observer中进行响应式的绑定,在数据被读的时候,触发get方法,执行Dep来收集依赖,也就是收集Watcher。

    在数据被改的时候,触发set方法,通过对应的所有依赖(Watcher),去执行更新。比如watch和computed就执行开发者自定义的回调方法。

    本篇文章属于入门篇,能够先简单的理解Observer、Dep、Watcher三者的作用和关系。后面会逐渐详细和深入,循序渐进的理解和学习。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持北冥有鱼。

    您可能感兴趣的文章:

    • 浅谈Vue的响应式原理
    • vue.js响应式原理解析与实现
    • Vue实现双向绑定的原理以及响应式数据的方法
    • 浅谈Vue 数据响应式原理
    • 代码详解Vuejs响应式原理
    • 通过图带你深入了解vue的响应式原理

    广而告之:
    热门推荐:
    javascript 对象的定义方法

    JS中定义类的方式有很多种: 1、工厂方式   function Car(){    var ocar = new Object;    ocar.color = "blue";    ocar.doors = 4;    ocar.s···

    js中prototype用法详细介绍

    prototype 是在 IE 4 及其以后版本引入的一个针对于某一类的对象的方法,而且特殊的地方便在于:它是一个给类的对象添加方法的方法!这一点可能听起来会有点乱,别急,下面我便通过实例对这一特殊的方法作已下讲解: 首先,我们要先了解一下类的概念,JavaScript 本身是···

    JS截取字符串实例详解

    本文实例讲述了JS截取字符串的方法。分享给大家供大家参考,具体如下: js截取字符串可使用 substring()或者slice()   函数:split() 功能:使用一个指定的分隔符把一个字符串分割存储到数组 例子: str="jpg|bmp|gif|ico|png"; arr=theString.split("|"); //arr是一个···

    火狐浏览器(firefox)下获得Event对象以及keyCode

    复制代码 代码如下:var isie = (document.all) ? true:false; var key; var ev; if(isie){ key = window.event.keyCode; ev = window.event; }else{ key = e.which; ev = e; } 这个时候可以alert(key) 看看, 但是 要是想用到 ev.returnValue = true; // IE ev.prevent···

    理解JSON:3分钟课程

    两个月前你从没听说过JSON 一个月前你听说了这个词但没有留意 一周前你发现这个词被提到多次,开始想,没错 … 又有一些垃圾东西要学了 今天你被心灵深处的一个闹铃闹醒,心想:这该死的json究竟是个什么东西?为什么突然间到处都是它了! 于是晚上我乘坐了一辆慢腾腾的···

    Javascript快速实现浏览器系统通知

    JS 实现浏览器的 title 闪烁、滚动、声音提示、chrome、Firefox、Safari等系统通知。 下载 $ npm install title-notify --save-dev $ bower install inotify --save-dev 编译 # 下载依赖工具 $ npm install # 压缩inotify $ npm build init effect: flash | scroll | fav···

    MySQL对数据库数据进行复制的基本过程详解

    复制      复制是从一个MySQL服务器(master)将数据拷贝到另外一台或多台MySQL服务器(slaves)的过程.复制是异步进行的--slaves服务器不需要持续地保持连接来接收master的数据.依据配置的不同,可以复制所有数据库,或指定的数据库,甚至是某一数据库指定的表. ···

    PHP 处理图片的类实现代码

    复制代码 代码如下: <?php /** * author:yagas * email:yagas60@21cn.com */ class Image { /** 类保护变量 */ protected $th_width = 100; protected $th_height = 50; protected $quality = 85; //图片质量 protected $transparent = 50; //水印透明度 protected $b···

    网站网页设计的一些小知识

    其实最近关于网页设计等问题一直都欧式不绝于耳的,毕竟现在互联网行业发展的这么好,而且网页的设计也是一种兴趣爱好,有的时候自己在看网页的时候,是不是有一种自己也想要去做一个网页的想法呢,而且在听说每年一些著名的网上商城都在进行着疯狂的交易的时候,是不是自己也···

    PHP访问MYSQL数据库封装类(附函数说明)

    复制代码 代码如下:<?php /* MYSQL 数据库访问封装类 MYSQL 数据访问方式,php4支持以mysql_开头的过程访问方式,php5开始支持以mysqli_开头的过程和mysqli面向对象 访问方式,本封装类以mysql_封装 数据访问的一般流程: 1,连接数据库 mysql_connect or mysql_pconne···