中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue.js中實現響應式的原理是什么

發布時間:2021-07-21 13:52:37 來源:億速云 閱讀:152 作者:Leah 欄目:web開發

Vue.js中實現響應式的原理是什么,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

一、Vue.js 響應式的使用

現在有個很簡單的需求,點擊頁面中 “leo” 文本后,文本內容修改為“你好,前端自習課”。

我們可以直接操作 DOM,來完成這個需求:

<span id="name">leo</span>
const node = document.querySelector('#name')  node.innerText = '你好,前端自習課';

實現起來比較簡單,當我們需要修改的數據有很多時(比如相同數據被多處引用),這樣的操作將變得復雜。

既然說到 Vue.js,我們就來看看 Vue.js 怎么實現上面需求:

<template>    <div id="app">      <span @click="setName">{{ name }}</span>    </div>  </template>  <script>  export default {    name: "App",    data() {      return {        name: "leo",      };    },    methods: {      setName() {        this.name = "你好,前端自習課";      },    },  };  </script>

觀察上面代碼,我們通過改變數據,來自動更新視圖。當我們有多個地方引用這個 name 時,視圖都會自動更新。

<template>    <div id="app">      <span @click="setName">{{ name }}</span>      <span>{{ name }}</span>      <span>{{ name }}</span>      <span>{{ name }}</span>    </div>  </template>

當我們使用目前主流的前端框架 Vue.js 和 React 開發業務時,只需關注頁面數據如何變化,因為數據變化后,視圖也會自動更新,這讓我們從繁雜的 DOM 操作中解脫出來,提高開發效率。

二、回顧觀察者模式

前面反復提到“通過改變數據,來自動更新視圖”,換個說法就是“數據改變后,使用該數據的地方被動發生響應,更新視圖”。

是不是有種熟悉的感覺?數據無需關注自身被多少對象引用,只需在數據變化時,通知到引用的對象即可,引用的對象作出響應。恩,有種觀察者模式的味道?

關于觀察者模式,可閱讀我之前寫的《圖解設計模式之觀察者模式(TypeScript)》。

1. 觀察者模式流程

觀察者模式表示一種“一對多”的關系,n 個觀察者關注 1 個被觀察者,被觀察者可以主動通知所有觀察者。接下圖:

Vue.js中實現響應式的原理是什么

在這張圖中,粉絲想及時收到“前端自習課”最新文章,只需關注即可,“前端自習課”有新文章,會主動推送給每個粉絲。該過程中,“前端自習課”是被觀察者,每位“粉絲”是觀察者。

2. 觀察者模式核心

觀察者模式核心組成包括:n 個觀察者和 1 個被觀察者。這里實現一個簡單觀察者模式:

2.1 定義接口

// 觀察目標接口  interface ISubject {      addObserver: (observer: Observer) => void; // 添加觀察者      removeObserver: (observer: Observer) => void; // 移除觀察者      notify: () => void; // 通知觀察者  }  // 觀察者接口  interface IObserver {      update: () => void;  }

2.2 實現被觀察者類

// 實現被觀察者類  class Subject implements ISubject {      private observers: IObserver[] = [];      public addObserver(observer: IObserver): void {          this.observers.push(observer);      }      public removeObserver(observer: IObserver): void {          const idx: number = this.observers.indexOf(observer);          ~idx && this.observers.splice(idx, 1);      }      public notify(): void {          this.observers.forEach(observer => {              observer.update();          });      }  }

2.3 實現觀察者類

// 實現觀察者類  class Observer implements IObserver {      constructor(private name: string) { }      update(): void {          console.log(`${this.name} has been notified.`);      }  }

2.4 測試代碼

function useObserver(){      const subject: ISubject = new Subject();      const Leo = new Observer("Leo");      const Robin = new Observer("Robin");      const Pual = new Observer("Pual");      subject.addObserver(Leo);      subject.addObserver(Robin);      subject.addObserver(Pual);      subject.notify();      subject.removeObserver(Pual);      subject.notify();  }  useObserver();  // [LOG]: "Leo has been notified."   // [LOG]: "Robin has been notified."   // [LOG]: "Pual has been notified."   // [LOG]: "Leo has been notified."   // [LOG]: "Robin has been notified."

三、回顧 Object.defineProperty()

Vue.js 的數據響應式原理是基于 JS 標準內置對象方法 Object.defineProperty() 方法來實現,該方法不兼容 IE8 和 FF22 及以下版本瀏覽器,這也是為什么 Vue.js 只能在這些版本之上的瀏覽器中才能運行的原因。

理解 Object.defineProperty() 對我們理解 Vue.js 響應式原理非常重要。

Vue.js 3 使用 proxy 方法實現響應式,兩者類似,我們只需搞懂Object.defineProperty() , proxy 也就差不多理解了。

1. 概念介紹

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,并返回此對象。

語法如下:

Object.defineProperty(obj, prop, descriptor)
  •  入參說明:

obj :要定義屬性的源對象;

prop :要定義或修改的屬性名稱或 Symbol;

descriptor :要定義或修改的屬性描述符,包括 configurable、enumerable、value、writable、get、set,具體的可以去參閱文檔;

  •  出參說明:

修改后的源對象。

舉個簡單?例子:

const leo = {};  Object.defineProperty(leo, 'age', {      value: 18,      writable: true  })  console.log(leo.age); // 18  leo.age = 22;  console.log(leo.age); // 22

2. 實現 getter/setter

我們知道 Object.defineProperty() 方法第三個參數是屬性描述符(descriptor),支持設置 get 和 set 描述符:

  •  get 描述符:當訪問該屬性時,會調用此函數,默認值為 undefined ;

  •  set 描述符:當修改該屬性時,會調用此函數,默認值為 undefined 。

一旦對象擁有了 getter/setter 方法,我們可以簡單將該對象稱為響應式對象。

這兩個操作符為我們提供攔截數據進行操作的可能性,修改前面示例,添加 getter/setter 方法:

let leo = {}, age = 18;  Object.defineProperty(leo, 'age', {       get(){          // to do something            console.log('監聽到請求數據');          return age;      },      set(newAge){          // to do something            console.log('監聽到修改數據');          age = newAge > age ? age : newAge      }  })  leo.age = 20;  // 監聽到修改數據  console.log(leo.age); // 監聽到請求數據  // 18  leo.age = 10;  // 監聽到修改數據  console.log(leo.age); // 監聽到請求數據  // 10

訪問 leo 對象的 age 屬性,會通過 get 描述符處理,而修改 age 屬性,則會通過 set 描述符處理。

四、實現簡單的數據響應式

通過前面兩個小節,我們復習了“觀察者模式”和“Object.defineProperty()” 方法,這兩個知識點在 Vue.js 響應式原理中非常重要。

接下來我們來實現一個很簡單的數據響應式變化,需求如下:點擊“更新數據”按鈕,文本更新。

Vue.js中實現響應式的原理是什么

接下來我們將實現三個類:

  •  Dep 被觀察者類,用來生成被觀察者;

  •  Watcher 觀察者類,用來生成觀察者;

  •  Observer 類,將普通數據轉換為響應式數據,從而實現響應式對象。

用一張圖來描述三者之間關系,現在看不懂沒關系,這小節看完可以再回顧這張圖:

Vue.js中實現響應式的原理是什么

1. 實現精簡觀察者模式

這里參照前面復習“觀察者模式”的示例,做下精簡:

// 實現被觀察者類  class Dep {      constructor() {          this.subs = [];      }      addSub(watcher) {          this.subs.push(watcher);      }      notify(data) {          this.subs.forEach(sub => sub.update(data));      }  }  // 實現觀察者類  class Watcher {      constructor(cb) {          this.cb = cb;      }      update(data) {          this.cb(data);      }  }

Vue.js 響應式原理中,觀察者模式起到非常重要的作用。其中:

  •  Dep 被觀察者類,提供用來收集觀察者( addSub )方法和通知觀察者( notify )方法;

  •  Watcher 觀察者類,實例化時支持傳入回調( cb )方法,并提供更新( update )方法;

2. 實現生成響應式的類

這一步需要實現 Observer 類,核心是通過 Object.defineProperty() 方法為對象的每個屬性設置 getter/setter,目的是將普通數據轉換為響應式數據,從而實現響應式對象。

Vue.js中實現響應式的原理是什么

這里以最簡單的單層對象為例(下一節會介紹深層對象),如:

let initData = {      text: '你好,前端自習課',      desc: '每日清晨,享受一篇前端優秀文章。'  };

接下來實現 Observer 類:

// 實現響應式類(最簡單單層的對象,暫不考慮深層對象)  class Observer {      constructor (node, data) {          this.defineReactive(node, data)      }      // 實現數據劫持(核心方法)      // 遍歷 data 中所有的數據,都添加上 getter 和 setter 方法      defineReactive(vm, obj) {          //每一個屬性都重新定義get、set          for(let key in obj){              let value = obj[key], dep = new Dep();              Object.defineProperty(obj, key, {                  enumerable: true,                  configurable: true,                  get() {                      // 創建觀察者                      let watcher = new Watcher(v => vvm.innerText = v);                      dep.addSub(watcher);                      return value;                  },                  set(newValue) {                      value = newValue;                      // 通知所有觀察者                      dep.notify(newValue);                  }              })          }      }  }

上面代碼的核心是 defineReactive 方法,它遍歷原始對象中每個屬性,為每個屬性實例化一個被觀察者(Dep),然后分別調用 Object.defineProperty() 方法,為每個屬性添加 getter/setter。

  •  訪問數據時,getter 執行依賴收集(即添加觀察者),通過實例化 Watcher 創建一個觀察者,并執行被觀察者的 addSub() 方法添加一個觀察者;

  •  修改數據時,setter 執行派發更新(即通知觀察者),通過調用被觀察者的 notify() 方法通知所有觀察者,執行觀察者 update() 方法。

3. 測試代碼

為了方便觀察數據變化,我們為“更新數據”按鈕綁定點擊事件來修改數據:

<div id="app"></div>  <button id="update">更新數據</button>

測試代碼如下:

// 初始化測試數據  let initData = {      text: '你好,前端自習課',     desc: '每日清晨,享受一篇前端優秀文章。'  };  const app = document.querySelector('#app');  // 步驟1:為測試數據轉換為響應式對象  new Observer(app, initData);  // 步驟2:初始化頁面文本內容  app.innerText = initData.text;  // 步驟3:綁定按鈕事件,點擊觸發測試  document.querySelector('#update').addEventListener('click', function(){      initData.text = `我們必須經常保持舊的記憶和新的希望。`;      console.log(`當前時間:${new Date().toLocaleString()}`)  })

測試代碼中,核心在于通過實例化 Observer,將測試數據轉換為響應式數據,然后模擬數據變化,來觀察視圖變化。

每次點擊“更新數據”按鈕,在控制臺中都能看到“數據發生變化!”的提示,說明我們已經能通過 setter 觀察到數據的變化情況。

Vue.js中實現響應式的原理是什么

當然,你還可以在控制臺手動修改 initData 對象中的 text 屬性,來體驗響應式變化~~

Vue.js中實現響應式的原理是什么

到這里,我們實現了非常簡單的數據響應式變化,當然 Vue.js 肯定沒有這么簡單,這個先理解,下一節看 Vue.js 響應式原理,思路就會清晰很多。

這部分代碼,我已經放到我的 Github,地址:https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Gist/Vue/Basics-Reactive-Demo.js

可以再回顧下這張圖,對整個過程會更清晰:

Vue.js中實現響應式的原理是什么

observer-watcher-dep.png

五、Vue.js 響應式實現

本節代碼:https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Gist/Vue/leo-vue-reactive/

這里大家可以再回顧下下面這張官網經典的圖,思考下前面講的示例。

Vue.js中實現響應式的原理是什么

(圖片來自:https://cn.vuejs.org/v2/guide/reactivity.html)

上一節實現了簡單的數據響應式,接下來繼續通過完善該示例,實現一個簡單的 Vue.js 響應式,測試代碼如下:

// index.js  const vm = new Vue({      el: '#app',      data(){          return {              text: '你好,前端自習課',              desc: '每日清晨,享受一篇前端優秀文章。'         }      }  });

是不是很有內味了,下面是我們最終實現后項目目錄:

- mini-reactive      / index.html   // 入口 HTML 文件    / index.js     // 入口 JS 文件    / observer.js  // 實現響應式,將數據轉換為響應式對象    / watcher.js   // 實現觀察者和被觀察者(依賴收集者)    / vue.js       // 實現 Vue 類作為主入口類    / compile.js   // 實現編譯模版功能

知道每一個文件功能以后,接下來將每一步串聯起來。

1. 實現入口文件

我們首先實現入口文件,包括 index.html / index.js  2 個簡單文件,用來方便接下來的測試。

1.1 index.html

<!DOCTYPE html>  <html lang="en">  <head>      <script src="./vue.js"></script>      <script src="./observer.js"></script>      <script src="./compile.js"></script>      <script src="./watcher.js"></script>  </head>  <body>      <div id="app">{{text}}</div>      <button id="update">更新數據</button>      <script src="./index.js"></script>  </body>  </html>

1.2 index.js

"use strict";  const vm = new Vue({      el: '#app',      data(){          return {              text: '你好,前端自習課',              desc: '每日清晨,享受一篇前端優秀文章。'          }      }  });  console.log(vm.$data.text)  vm.$data.text = '頁面數據更新成功!'; // 模擬數據變化  console.log(vm.$data.text)

2. 實現核心入口 vue.js

vue.js 文件是我們實現的整個響應式的入口文件,暴露一個 Vue 類,并掛載全局。

class Vue {      constructor (options = {}) {          this.$el = options.el;          this.$data = options.data();          this.$methods = options.methods;          // [核心流程]將普通 data 對象轉換為響應式對象          new Observer(this.$data);          if (this.$el) {              // [核心流程]將解析模板的內容              new Compile(this.$el, this)          }      }  }  window.Vue = Vue;

Vue 類入參為一個配置項 option ,使用起來跟 Vue.js 一樣,包括 $el 掛載點、 $data 數據對象和 $methods 方法列表(本文不詳細介紹)。

通過實例化 Oberser 類,將普通 data 對象轉換為響應式對象,然后判斷是否傳入 el 參數,存在時,則實例化 Compile 類,解析模版內容。

總結下 Vue 這個類工作流程 :

Vue.js中實現響應式的原理是什么

3. 實現 observer.js

observer.js 文件實現了 Observer 類,用來將普通對象轉換為響應式對象:

class Observer {      constructor (data) {          this.data = data;          this.walk(data);      }      // [核心方法]將 data 對象轉換為響應式對象,為每個 data 屬性設置 getter 和 setter 方法      walk (data) {          if (typeof data !== 'object') return data;          Object.keys(data).forEach( key => {              this.defineReactive(data, key, data[key])          })      }      // [核心方法]實現數據劫持      defineReactive (obj, key, value) {          this.walk(value);  // [核心過程]遍歷 walk 方法,處理深層對象。          const dep = new Dep();          Object.defineProperty(obj, key, {              enumerable: true,              configurable: true,              get () {                  console.log('[getter]方法執行')                  Dep.target &&  dep.addSub(Dep.target);                  return value              },              set (newValue) {                  console.log('[setter]方法執行')                  if (value === newValue) return;                  // [核心過程]當設置的新值 newValue 為對象,則繼續通過 walk 方法將其轉換為響應式對象                  if (typeof newValue === 'object') this.walk(newValue);                  value = newValue;                  dep.notify(); // [核心過程]執行被觀察者通知方法,通知所有觀察者執行 update 更新              }          })      }  }

相比較第四節實現的 Observer 類,這里做了調整:

  •  增加 walk 核心方法,用來遍歷對象每個屬性,分別調用數據劫持方法( defineReactive() );

  •  在 defineReactive() 的 getter 中,判斷 Dep.target 存在才添加觀察者,下一節會詳細介紹 Dep.target;

  •  在 defineReactive() 的 setter 中,判斷當前新值( newValue )是否為對象,如果是,則直接調用 this.walk() 方法將當前對象再次轉為響應式對象,處理深層對象。

通過改善后的 Observer 類,我們就可以實現將單層或深層嵌套的普通對象轉換為響應式對象。

4. 實現 watcher.js

這里實現了 Dep 被觀察者類(依賴收集者)和 Watcher 觀察者類。

class Dep {      constructor() {          this.subs = [];      }      addSub(watcher) {          this.subs.push(watcher);      }      notify(data) {          this.subs.forEach(sub => sub.update(data));      }  }  class Watcher {      constructor (vm, key, cb) {          this.vm = vm;   // vm:表示當前實例          this.key = key; // key:表示當前操作的數據名稱          this.cb = cb;   // cb:表示數據發生改變之后的回調          Dep.target = this; // 全局唯一          thisthis.oldValue = this.vm.$data[key]; // 保存變化的數據作為舊值,后續作判斷是否更新          Dep.target = null;      }      update () {          console.log(`數據發生變化!`);          let oldValue = this.oldValue;          let newValue = this.vm.$data[this.key];          if (oldValue != newValue) {  // 比較新舊值,發生變化才執行回調              this.cb(newValue, oldValue);          };      }  }

相比較第四節實現的 Watcher  類,這里做了調整:

  •  在構造函數中,增加 Dep.target 值操作;

  •  在構造函數中,增加 oldValue 變量,保存變化的數據作為舊值,后續作為判斷是否更新的依據;

  •  在 update() 方法中,增加當前操作對象 key 對應值的新舊值比較,如果不同,才執行回調。

Dep.target 是當前全局唯一的訂閱者,因為同一時間只允許一個訂閱者被處理。target 指當前正在處理的目標訂閱者,當前訂閱者處理完就賦值為 null 。這里 Dep.target 會在 defineReactive() 的 getter 中使用到。

通過改善后的 Watcher 類,我們操作當前操作對象 key 對應值的時候,可以在數據有變化的情況才執行回調,減少資源浪費。

4. 實現 compile.js

compile.js 實現了 Vue.js 的模版編譯,如將 HTML 中的 {{text}} 模版轉換為具體變量的值。

compile.js 介紹內容較多,考慮到篇幅問題,并且本文核心介紹響應式原理,所以這里就暫時不介紹 compile.js 的實現,在學習的朋友可以到我 Github 上下載該文件直接下載使用即可,地址:

https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Gist/Vue/leo-vue-reactive/compile.js

5. 測試代碼

到這里,我們已經將第四節的 demo 改造成簡易版 Vue.js 響應式,接下來打開 index.html 看看效果:

Vue.js中實現響應式的原理是什么

當 index.js 中執行到:

vm.$data.text = '我們必須經常保持舊的記憶和新的希望。';

看完上述內容,你們掌握Vue.js中實現響應式的原理是什么的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

济阳县| 潢川县| 富裕县| 潼关县| 绍兴市| 吉水县| 方城县| 临沂市| 海原县| 吉木乃县| 新乡县| 于田县| 平顶山市| 安阳市| 卓尼县| 永仁县| 临颍县| 昌宁县| 扬中市| 当阳市| 琼中| 通州市| 延津县| 芒康县| 收藏| 东港市| 阜宁县| 稷山县| 静宁县| 和平区| 新巴尔虎右旗| 崇阳县| 渑池县| 常宁市| 化州市| 兰考县| 开封市| 五台县| 东乡| 盐津县| 凤冈县|