最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

ES6 Proxy實現(xiàn)Vue的變化檢測問題

 更新時間:2019年06月11日 08:32:09   作者:浪子閑人  
Vue3.0將采用ES6 Proxy的形式重新實現(xiàn)Vue的變化檢測,在官方還沒給出新方法之前,我們先實現(xiàn)一個基于Proxy的變化檢測。感興趣的朋友跟隨小編一起看看吧

Vue變化檢測Object使用DefineProperty、數(shù)組使用方法攔截實現(xiàn)。最近,Vue3.0將采用ES6 Proxy的形式重新實現(xiàn)Vue的變化檢測,在官方還沒給出新方法之前,我們先實現(xiàn)一個基于Proxy的變化檢測。

模塊劃分

參照之前Vue變化檢測的代碼,將Vue 變化檢測的功能分為以下幾個部分。

  • Observer
  • Dep
  • Watcher
  • Utils

首先,我們要確定的問題是,將Dep依賴搜集存在哪里。Vue 2.x里,Object的依賴收集放在defineRactive,Array的依收集存入到Observer中。ES6 Proxy里,考慮到讓handler訪問dep,我們將依賴放入到Observer中。

Observer

observer.js功能代碼如下:

import Dep from './dep';
import { isObject } from './utils';
export default class Observer {
  constructor (value) {
    // 遞歸處理子元素
    this.obeserve(value);
    // 實現(xiàn)當前元素的代理
    this.value = this.proxyTarget(value);
  }
  proxyTarget (targetBefore, keyBefore) {
    const dep = new Dep();
    targetBefore.__dep__ = dep;
    let self = this;
    const filtersAtrr = val => ['__dep__', '__parent__'].indexOf(val) > -1;
    return new Proxy(targetBefore, {
      get: function(target, key, receiver){
        if (filtersAtrr(key)) return Reflect.get(target, key, receiver);
        if (!Array.isArray(target)) {
          dep.depend(key);
        }
        // sort/reverse等不改變數(shù)組長度的,在get里觸發(fā)
        if (Array.isArray(target)) {
          if ((key === 'sort' || key === 'reverse') && target.__parent__) {
            target.__parent__.__dep__.notify(keyBefore);
          }
        } 
        return Reflect.get(target, key, receiver);
      },
      set: function(target, key, value, receiver){
        if (filtersAtrr(key)) return Reflect.set(target, key, value, receiver);
        // 新增元素,需要proxy
        const { newValue, isChanged } = self.addProxyTarget(value, target, key, self);
        // 設置key為新元素
        Reflect.set(target, key, newValue, receiver);
        // notify
        self.depNotify(target, key, keyBefore, dep, isChanged);
        return true;
      },
    });
  }
  addProxyTarget(value, target, key, self) {
    let newValue = value;
    let isChanged = false;
    if (isObject(value) && !value.__parent__) {
      self.obeserve(newValue);
      newValue = self.proxyTarget(newValue, key);
      newValue.__parent__ = target;
      isChanged = true;
    }
    return {
      newValue,
      isChanged,
    }
  }
  depNotify(target, key, keyBefore, dep, isChanged) {
    if (isChanged && target.__parent__) {
      target.__parent__.__dep__.notify(keyBefore);
      return;
    }
    if (Array.isArray(target)) {
      if (key === 'length' && target.__parent__) {
        target.__parent__.__dep__.notify(keyBefore);
      }
    } else {
      dep.notify(key);
    }
  }
  obeserve(target) {
    // 只處理對象類型,包括數(shù)組、對象
    if (!isObject(target)) return;
    for (let key in target) {
      if (isObject(target[key]) && target[key] !== null) {
        this.obeserve(target[key]);
        target[key] = this.proxyTarget(target[key], key);
        // 設置__parent__,方便子元素調用
        target[key].__parent__ = target;
      }
    }
  }
}

在Observer中,針對對象,只需要執(zhí)行 dep.depend(key) 、 dep.notify(key) 即可。添加 key 是為了能正確的觸發(fā)收集,不知道怎么說明白為什么要這樣做,只能一切盡在不言中了。

Array, 如何實現(xiàn)依賴的收集和觸發(fā)那。依賴收集與Object類似, dep.depend(key) 完成數(shù)組的收集。關于觸發(fā),可以分為兩個方面,一是改變數(shù)組長度、二未改變數(shù)組長度的。改變數(shù)組長度的,在set里,通過長度屬性的設置觸發(fā)父級元素的notify。為什么要使用父級元素的notify那?我們可以分析以下,在你設置數(shù)組的長度時,這時候的target\key\value分別是[]\length*, 這個時候,數(shù)組的依賴收集是沒有的,你watcher的是數(shù)組,并不是數(shù)組本身。這個時候只能通過 target.__parent__.__dep__.notify(keyBefore) 觸發(fā)父級的收集,完成數(shù)據(jù)變化的檢測。二對于未改變數(shù)組長度的,這里的做法,雖然是直接 target.__parent__.__dep__.notify(keyBefore) 觸發(fā)依賴,但是有個嚴重的問題,實際上更新的數(shù)據(jù)不是最新的,這個地方暫時還沒想到比較好的方法,歡迎大家討論。

Dep

Dep.js

let uid = 0;
export default class Dep {
  constructor () {
    this.subs = {};
    this.id = uid++;
  }
  addSub(prop, sub) {
    this.subs[prop] = this.subs[prop] || [];
    this.subs[prop].push(sub);
  }
  removeSub(prop, sub) {
    this.remove(this.subs[prop] || [], sub);
  }
  depend(prop) {
    if (Dep.target) {
      // 傳入的是當前依賴
      Dep.target.addDep(prop, this)
    }
  }
  notify(prop) {
    const subs = (this.subs[prop] || []).slice();
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
  }
  remove(arr, item) {
    if (arr.length) {
      const index = arr.indexOf(item);
      if (index > -1) {
        return arr.splice(index, 1);
      }
    }
  }
}
Dep.target = null
const targetStack = []
export function pushTarget (_target) {
  if (Dep.target) targetStack.push(Dep.target)
  Dep.target = _target
}
export function popTarget () {
  Dep.target = targetStack.pop()
}

dep 添加prop實現(xiàn)類型的綁定,為什么要這么做那?使用proxy代理后,你假如wahcter對象下的幾個元素,此時的deps將同時存在這幾個元素,你觸發(fā)依賴的時候,這些依賴都會執(zhí)行。因此,通過key值綁定觀察事件,觸發(fā)時,能完成對象的正確觸發(fā)。

watcher、utils

import { parsePath } from './utils';
import { pushTarget, popTarget } from './dep'
export default class Watcher {
  constructor(vm, expOrFn, cb) {
    // dep id集合
    this.depIds = new Set();
    this.vm = vm;
    this.getter = parsePath(expOrFn);
    this.cb = cb;
    this.value = this.get();
  }
  get () {
    pushTarget(this);
    let value = this.getter.call(this.vm, this.vm);
    popTarget();
    return value;
  }
  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.cb.call(this.vm, this.value, oldValue);
  }
  addDep (prop, dep) {
    const id = dep.id;
    if (!this.depIds.has(id)) {
      this.depIds.add(id);
      dep.addSub(prop, this);
    }
  }
}

utils.js

/**
 * 解析簡單路徑
 */
const bailRE = /[^\w.$]/;
export function parsePath (path) {
  if (bailRE.test(path)) {
    return;
  }
  const segments = path.split('.');
  return function (obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return;
      obj = obj[segments[i]];
    }
    return obj;
  };
}
/**
 * Define a property.
 */
export function def (obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  })
}
/**
 * Quick object check - this is primarily used to tell
 * Objects from primitive values when we know the value
 * is a JSON-compliant type.
 */
export function isObject (obj) {
  return obj !== null && typeof obj === 'object'
}
/**
 * Check whether an object has the property.
 */
const hasOwnProperty = Object.prototype.hasOwnProperty
export function hasOwn (obj, key) {
 return hasOwnProperty.call(obj, key)
}

Utils.js/Watchers.js與Vue 2.x類似,這里就不多介紹了。

測試一下

test.js

import Observer from './observer';
import Watcher from './watcher';
let data = {
  name: 'lijincai',
  password: '***********',
  address: {
    home: '安徽亳州譙城區(qū)',
  },
  list: [{
    name: 'lijincai',
    password: 'you know it Object',
  }], 
};
const newData = new Observer(data);
let index = 0;
const watcherName = new Watcher(newData, 'value.name', (newValue, oldValue) => {
  console.log(`${index++}: name newValue:`, newValue, ', oldValue:', oldValue);
});
const watcherPassword = new Watcher(newData, 'value.password', (newValue, oldValue) => {
  console.log(`${index++}: password newValue:`, newValue, ', oldValue:', oldValue);
});
const watcherAddress = new Watcher(newData, 'value.address', (newValue, oldValue) => {
  console.log(`${index++}: address newValue:`, newValue, ', oldValue:', oldValue);
});
const watcherAddressHome = new Watcher(newData, 'value.address.home', (newValue, oldValue) => {
  console.log(`${index++}: address.home newValue:`, newValue, ', oldValue:', oldValue);
});
const watcherAddProp = new Watcher(newData, 'value.addProp', (newValue, oldValue) => {
  console.log(`${index++}: addProp newValue:`, newValue, ', oldValue:', oldValue);
});
const watcherDataObject = new Watcher(newData, 'value.list', (newValue, oldValue) => {
  console.log(`${index++}: newValue:`, newValue, ', oldValue:', oldValue);
});
newData.value.name = 'resetName';
newData.value.password = 'resetPassword';
newData.value.name = 'hello world name';
newData.value.password = 'hello world password';
newData.value.address.home = 'hello home';
newData.value.address.home = 'hello home2';
newData.value.addProp = 'hello addProp';
newData.value.addProp ={
  name: 'ceshi',
};
newData.value.addProp.name = 'ceshi2';
newData.value.list.push('1');
newData.value.list.splice(0, 1);
newData.value.list.sort();
newData.value.list.reverse();
newData.value.list.push('1');
newData.value.list.unshift({name: 'nihao'});
newData.value.list[0] = {
  name: 'lijincai',
  password: 'you know it Array',
};
newData.value.list[0].name = 'you know it array after';
newData.value.list.pop();
newData.value.list.shift();
newData.value.list.length = 1;

我們使用對象、數(shù)組測試一下我們的ES6 Proxy檢測。

20:17:44.725 index.js?afc7:20 0: name newValue: resetName , oldValue: lijincai
20:17:44.725 index.js?afc7:24 1: password newValue: resetPassword , oldValue: ***********
20:17:44.725 index.js?afc7:20 2: name newValue: hello world name , oldValue: resetName
20:17:44.725 index.js?afc7:24 3: password newValue: hello world password , oldValue: resetPassword
20:17:44.726 index.js?afc7:32 4: address.home newValue: hello home , oldValue: 安徽亳州譙城區(qū)
20:17:44.726 index.js?afc7:32 5: address.home newValue: hello home2 , oldValue: hello home
20:17:44.726 index.js?afc7:36 6: addProp newValue: hello addProp , oldValue: undefined
20:17:44.727 index.js?afc7:36 7: addProp newValue: Proxy {name: "ceshi", __dep__: Dep, __parent__: {…}} , oldValue: hello addProp
20:17:44.727 index.js?afc7:40 0: newValue: Proxy {0: Proxy, 1: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", __dep__: Dep, __parent__: {…}}
20:17:44.728 index.js?afc7:40 1: newValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}}
20:17:44.729 index.js?afc7:40 2: newValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}}
20:17:44.731 index.js?afc7:40 3: newValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}}
20:17:44.734 index.js?afc7:40 4: newValue: Proxy {0: "1", 1: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", 1: "1", __dep__: Dep, __parent__: {…}}
20:17:44.735 index.js?afc7:40 5: newValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}}
20:17:44.735 index.js?afc7:40 6: newValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}}
20:17:44.736 index.js?afc7:40 7: newValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}}
20:17:44.737 index.js?afc7:40 8: newValue: Proxy {0: Proxy, 1: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", __dep__: Dep, __parent__: {…}}
20:17:44.738 index.js?afc7:40 9: newValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}}
20:17:44.738 index.js?afc7:40 10: newValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}}

我們看到了ES6 Proxy后實現(xiàn)了Object/Array的檢測,雖然還存在一些問題,但是基本的偵測變化的功能都已經(jīng)具備了。

總結

以上所述是小編給大家介紹的ES6 Proxy實現(xiàn)Vue的變化檢測問題,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!

相關文章

  • vue3動態(tài)路由+菜單欄的實現(xiàn)示例

    vue3動態(tài)路由+菜單欄的實現(xiàn)示例

    在后臺管理系統(tǒng),可以根據(jù)登錄用戶的不同返回不同路由,頁面也會根據(jù)這些路由生成對應的菜單,本文主要介紹了vue3動態(tài)路由+菜單欄的實現(xiàn)示例,感興趣的可以了解一下
    2024-04-04
  • vue3中使用router路由實現(xiàn)跳轉傳參的方法

    vue3中使用router路由實現(xiàn)跳轉傳參的方法

    這篇文章主要介紹了vue3中使用router路由實現(xiàn)跳轉傳參的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • vue實現(xiàn)element上傳多張圖片瀏覽刪除功能

    vue實現(xiàn)element上傳多張圖片瀏覽刪除功能

    這篇文章主要介紹了vue實現(xiàn)element上傳多張圖片瀏覽刪除功能,本文結合示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-10-10
  • vue?內置組件?component?的用法示例詳解

    vue?內置組件?component?的用法示例詳解

    這篇文章主要介紹了vue內置組件component的用法,本文給大家介紹了component內置組件切換方法,通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • Vue中使用?Aplayer?和?Metingjs?添加音樂插件的方式

    Vue中使用?Aplayer?和?Metingjs?添加音樂插件的方式

    這篇文章主要介紹了Vue中使用?Aplayer?和?Metingjs?添加音樂插件,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • vue3中安裝并使用CSS預處理器Sass的方法詳解

    vue3中安裝并使用CSS預處理器Sass的方法詳解

    Sass是一種CSS預處理器,它擴展了CSS的功能,提供了更高級的語法和特性,例如變量、嵌套、混合、繼承和顏色功能等,這些特性可以幫助開發(fā)者更高效地管理和維護樣式表,本文介紹vue3中安裝并使用CSS預處理器Sass的方法,感興趣的朋友一起看看吧
    2024-01-01
  • vue中如何去掉input前后的空格

    vue中如何去掉input前后的空格

    這篇文章主要介紹了vue中如何去掉input前后的空格問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • vue中swiper?vue-awesome-swiper的使用方法及各種坑解決

    vue中swiper?vue-awesome-swiper的使用方法及各種坑解決

    這篇文章主要介紹了vue中swiper?vue-awesome-swiper的使用方法及各種坑解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Vue與Node.js通過socket.io通信的示例代碼

    Vue與Node.js通過socket.io通信的示例代碼

    這篇文章主要介紹了Vue與Node.js通過socket.io通信的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • vue性能優(yōu)化之cdn引入vue-Router的問題

    vue性能優(yōu)化之cdn引入vue-Router的問題

    這篇文章主要介紹了vue性能優(yōu)化之cdn引入vue-Router的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評論

望城县| 铁岭县| 景德镇市| 青神县| 金昌市| 枣强县| 张家川| 潮安县| 青冈县| 凉城县| 通海县| 永胜县| 密山市| 涟源市| 徐州市| 尼木县| 雷山县| 温州市| 陆河县| 平阳县| 印江| 张家界市| 深州市| 万载县| 叶城县| 云林县| 江津市| 兴山县| 临泉县| 南昌市| 玉环县| 罗城| 长沙市| 德惠市| 博乐市| 屯昌县| 杭锦旗| 聂拉木县| 万年县| 峡江县| 马龙县|