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

溫馨提示×

溫馨提示×

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

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

vue中怎么實現觀察者模式

發布時間:2021-07-09 15:17:27 來源:億速云 閱讀:261 作者:Leah 欄目:web開發

vue中怎么實現觀察者模式,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

1 響應式原理

讓我們先從相應式原理開始。我們可以通過Object.defineProterty()來自定義Object的getter和setter 從而達到我們的目的。

代碼如下

function observe(value, cb) {
 Object.keys(value).forEach((key) => defineReactive(value, key, value[key] , cb))
}

function defineReactive (obj, key, val, cb) {
 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: ()=>{
   /*....依賴收集等....*/
   /*Github:https://github.com/answershuto*/
   return val
  },
  set:newVal=> {
   val = newVal;
   cb();/*訂閱者收到消息的回調*/
  }
 })
}

class Vue {
 constructor(options) {
  this._data = options.data;
  observe(this._data, options.render)
 }
}

let app = new Vue({
 el: '#app',
 data: {
  text: 'text',
  text2: 'text2'
 },
 render(){
  console.log("render");
 }
})

通過observe函數對app.data上的每一個key和value都設定getter和setter。當value改變的時候觸發setter,就會觸發render這個函數。響應式的目的就達成,如果是視圖更新的話我們通過監聽dom的input事件來觸發數據更新
但是現在我們只有在改變vue._data.text的時候才會觸發他們的setter,但是我想偷懶,只改變vue.text就能觸發到setter怎么做呢?

我們使用代理的方法

_proxy.call(this, options.data);/*構造函數中*/

/*代理*/
function _proxy (data) {
 const that = this;
 Object.keys(data).forEach(key => {
  Object.defineProperty(that, key, {
   configurable: true,
   enumerable: true,
   get: function proxyGetter () {
    return that._data[key];
   },
   set: function proxySetter (val) {
    that._data[key] = val;
   }
  })
 });
}

依賴收集

讓我們再來看看下面的代碼

new Vue({
 template: 
  `<div>
   <span>text1:</span> {{text1}}
   <span>text2:</span> {{text2}}
  <div>`,
 data: {
  text1: 'text1',
  text2: 'text2',
  text3: 'text3'
 }
});

當你的text3變化的時候,實際上text3并沒有被渲染,但是也會觸發一次render函數,這顯然是不對的。所以我們需要收集依賴。

我們只需要在初始化的時候渲染一遍,那所有渲染所依賴的數據都會被觸發getter,這時候我們只要把這個數據放到一個列表里就好啦!

我們先來認識一下Dep(dependencies)這個類,下圖是一個最簡單的Dep類。我們可以把他理解為發布者(這點很重要!!)

class Dep {
 constructor () {
  this.subs = [];
 }

 addSub (sub: Watcher) {
  this.subs.push(sub)
 }

 removeSub (sub: Watcher) {
  remove(this.subs, sub)
 }
 /*Github:https://github.com/answershuto*/
 notify () {
  // stabilize the subscriber list first
  const subs = this.subs.slice()
  for (let i = 0, l = subs.length; i < l; i++) {
   subs[i].update()
  }
 }
}
function remove (arr, item) {
 if (arr.length) {
  const index = arr.indexOf(item)
  if (index > -1) {
   return arr.splice(index, 1)
 }
}

我們每次觸發getter的時候,只要把觸發的對象放到dep.sub里面就好啦!
但是現在問題來了,我們用什么來裝這個觸發的'對象',也可以說式訂閱者呢?

我們使用Watcher這個類

class Watcher {
 constructor (vm, expOrFn, cb, options) {
  this.cb = cb;
  this.vm = vm;

  /*在這里將觀察者本身賦值給全局的target,只有被target標記過的才會進行依賴收集*/
  Dep.target = this;
  /*Github:https://github.com/answershuto*/
  /*觸發渲染操作進行依賴收集*/
  this.cb.call(this.vm);
 }

 update () {
  this.cb.call(this.vm);
 }
}

vm即是vue實例, expOrFn就是{{a+b}}里面的a+b, cb就是回調函數就是return a+b, options是一些配置項。

Vue在第一次渲染列表的時候如果碰到{{xxx}}這樣的表達式,就會new Watcher()。解析里面的函數,然后把當前的watcher實例賦給Dep.target(Dep.target是全局的,一次性只能有一個存在,因為Vue一次只處理一個依賴)。然后執行回調函數。(這里看似是執行回調函數渲染,其實又觸發了一次getter,然后就會把當前的依賴添加到sub里去)

接下來開始依賴收集

class Vue {
 constructor(options) {
  this._data = options.data;
  observer(this._data, options.render);
  let watcher = new Watcher(this, );
 }
}

function defineReactive (obj, key, val, cb) {
 /*在閉包內存儲一個Dep對象*/
 const dep = new Dep();

 Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  get: ()=>{
   if (Dep.target) {
    /*Watcher對象存在全局的Dep.target中*/
    dep.addSub(Dep.target);
   }
  },
  set:newVal=> {
   /*只有之前addSub中的函數才會觸發*/
   dep.notify();
  }
 })
}

Dep.target = null; //防止依賴重復添加

這兒我們通過示例來講解

<template>
 <div>
 {{a+b}}
 </div>
 <div>
 {{a-c}}
 </div>
</template>

<script>
let app = new Vue( {
 data :{
  a: 1,
  b: 2,
  c: 3
 }
 })

我們編譯到{{a+b}},會去實例化一個對應的Watcher對象,Watcher的構造函數中有這么一句

this.cb.call(this.vm);this.cb指的是function(){return a+b};this.vm指的是這個vue對象,這樣就會觸發vue.a和vue.b的getter方法,a,b都有自己的dep對象,我們通過Dep.target將這個Watcher對象就加到dep的subs數組中去了,當我們變更a或者b是就會觸發setter,進而觸發subs數組中的update方法,視圖中的a+b就會更新

有個小知識點:我們新建一個屬性對象時必須通過Vue.set的方法去實現,而不能直接通過=實現,這樣會檢測不到,因為我們在初始化時就通過defineProperty重構了這個對象屬性的getter和setter方法,新建的屬性則沒有所以不會被檢測到

下圖為Vue框架在數據初始化中使用觀察者模式的示意圖:

vue中怎么實現觀察者模式

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

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

vue
AI

潞城市| 蕲春县| 肥西县| 绥滨县| 洪洞县| 都兰县| 竹溪县| 东台市| 阆中市| 呼和浩特市| 修文县| 唐山市| 衡山县| 峨眉山市| 新绛县| 赤水市| 米脂县| 高邮市| 五大连池市| 博乐市| 沙田区| 于田县| 科尔| 乌恰县| 咸阳市| 马龙县| 台东市| 泾川县| 西峡县| 新沂市| 克拉玛依市| 屯昌县| 石嘴山市| 肇庆市| 满洲里市| 陇南市| 宜黄县| 临高县| 嘉兴市| 武隆县| 景泰县|