搜尋
首頁web前端js教程vue.js響應式原理的深入理解
vue.js響應式原理的深入理解Oct 20, 2018 pm 04:45 PM
jsvue.js

這篇文章帶給大家的內容是關於vue.js響應式原理的深入理解,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

從很久之前就已經接觸過了angularjs了,當時就已經了解到,angularjs是透過髒檢查來實現資料監測以及頁面更新渲染。之後,再接觸了vue.js,當時也一度很好奇vue.js是如何監控資料更新並且重新渲染頁面。今天,就我們就來一步步解析vue.js響應式的原理,並且來實作一個簡單的demo。

首先,先讓我們來了解一些基礎知識。

基礎

Object.defineProperty

es5新增了Object.defineProperty這個api,它可以讓我們為物件的屬性設定getter和setter ,從而我們可以劫持使用者對物件屬性的取值與賦值。例如以下程式碼:

const obj = {
};

let val = 'cjg';                      //前端全栈学习交流群:866109386                  
Object.defineProperty(obj, 'name', { //面向想从事前端开发1到5年及以上工作经验的开发人员
  get() {                           // 帮助突破技术瓶颈,提升思维,欢迎大家进群交流。
    console.log('劫持了你的取值操作啦');
    return val;
  },
  set(newVal) {
    console.log('劫持了你的赋值操作啦');
    val = newVal;
  }
});
console.log(obj.name);
obj.name = 'cwc';
console.log(obj.name);
我們透過Object.defineProperty劫持了obj[name]的取值和賦值操作,因此我們就可以在這裡做一些手腳啦,比如說,我們可以在obj[name]被賦值的時候觸發更新頁面操作。

發布訂閱模式

發布訂閱模式是設計模式中比較常見的一種,其中有兩個角色:發布者和訂閱者。多個訂閱者可以向同一發布者訂閱一個事件,當事件發生的時候,發布者通知所有訂閱該事件的訂閱者。我們來看一個例子來了解下。

class Dep {             //前端全栈学习交流群:866109386                  
  constructor() {     //面向想从事前端开发1到5年及以上工作经验的开发人员
    this.subs = [];  // 帮助突破技术瓶颈,提升思维,欢迎大家进群交流。
  }
  // 增加订阅者
  addSub(sub) {
    if (this.subs.indexOf(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(); // 通知订阅者事件发生,触发他们的更新函数
動手實作

我們了解了Object.defineProperty和發布訂閱者模式後,我們不難可以想到,vue.js是基於以上兩者來實現數據監聽的。

vue.js先透過Object.defineProperty來對要監聽的資料進行getter和setter劫持,當資料的屬性被賦值/取值的時候,vue.js就可以察覺到並做對應的處理。


透過訂閱發布模式,我們可以為物件的每個屬性都建立一個發布者,當有其他訂閱者依賴這個屬性的時候,則將訂閱者加入到發布者的佇列中。利用Object.defineProperty的資料劫持,在屬性的setter呼叫的時候,該屬性的發布者通知所有訂閱者更新內容。

接下來,我們來實作(詳情可以看註解):

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',//前端全栈学习交流群:866109386                  
    *     age: 22,  //面向想从事前端开发1到5年及以上工作经验的开发人员
    *     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)  {
      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 = null;
    return this.value;
  }

  update() {
    const oldValue = this.value;   // 前端全栈学习交流群:866109386
    const newValue = this.get();  // 面向想从事前端开发1到5年及以上工作经验的开发人员
    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', (oldValue, newValue) => {
  console.log(oldValue, newValue);
})

data.name = 'zht';

// 监听data对象的obj.name属性,当data.obj.name发现变化的时候,触发cb函数
new Watcher(data, 'obj.name', (oldValue, newValue) => {
  console.log(oldValue, newValue);
})

data.obj.name = 'cwc';
data.obj.name = 'dmh';
結語

這樣,一個簡單的響應式資料監聽就完成了。當然,這也只是一個簡單的demo,來說明vue.js響應式的原理,真實的vue.js原始碼會更加複雜,因為加了很多其他邏輯。 ###

以上是vue.js響應式原理的深入理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:CSDN。如有侵權,請聯絡admin@php.cn刪除
总结分享几个 VueUse 最佳组合,快来收藏使用吧!总结分享几个 VueUse 最佳组合,快来收藏使用吧!Jul 20, 2022 pm 08:40 PM

VueUse 是 Anthony Fu 的一个开源项目,它为 Vue 开发人员提供了大量适用于 Vue 2 和 Vue 3 的基本 Composition API 实用程序函数。本篇文章就来给大家分享几个我常用的几个 VueUse 最佳组合,希望对大家有所帮助!

5 款适合国内使用的 Vue 移动端 UI 组件库5 款适合国内使用的 Vue 移动端 UI 组件库May 05, 2022 pm 09:11 PM

本篇文章给大家分享5 款适合国内使用的 Vue 移动端 UI 组件库,希望对大家有所帮助!

聊聊Vue3+qrcodejs如何生成二维码并添加文字描述聊聊Vue3+qrcodejs如何生成二维码并添加文字描述Aug 02, 2022 pm 09:19 PM

Vue3如何更好地使用qrcodejs生成二维码并添加文字描述?下面本篇文章给大家介绍一下Vue3+qrcodejs生成二维码并添加文字描述,希望对大家有所帮助。

手把手带你利用vue3.x绘制流程图手把手带你利用vue3.x绘制流程图Jun 08, 2022 am 11:57 AM

利用vue3.x怎么绘制流程图?下面本篇文章给大家分享基于 vue3.x 的流程图绘制方法,希望对大家有所帮助!

如何使用VueRouter4.x?快速上手指南如何使用VueRouter4.x?快速上手指南Jul 13, 2022 pm 08:11 PM

如何使用VueRouter4.x?下面本篇文章就来给大家分享快速上手教程,介绍一下10分钟快速上手VueRouter4.x的方法,希望对大家有所帮助!

聊聊vue指令中的修饰符,常用事件修饰符总结聊聊vue指令中的修饰符,常用事件修饰符总结May 09, 2022 am 11:07 AM

本篇文章带大家聊聊vue指令中的修饰符,对比一下vue中的指令修饰符和dom事件中的event对象,介绍一下常用的事件修饰符,希望对大家有所帮助!

Github 上 8 个不可错过的 Vue 项目,快来收藏!!Github 上 8 个不可错过的 Vue 项目,快来收藏!!Jun 17, 2022 am 10:37 AM

本篇文章给大家整理分享8个GitHub上很棒的的 Vue 项目,都是非常棒的项目,希望当中有您想要收藏的那一个。

如何覆盖组件库样式?React和Vue项目的解决方法浅析如何覆盖组件库样式?React和Vue项目的解决方法浅析May 16, 2022 am 11:15 AM

如何覆盖组件库样式?下面本篇文章给大家介绍一下React和Vue项目中优雅地覆盖组件库样式的方法,希望对大家有所帮助!

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境