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

詳解Vue 如何監(jiān)聽Array的變化

 更新時間:2019年06月06日 09:11:43   作者:蒼耳QAQ  
這篇文章主要介紹了詳解Vue 如何監(jiān)聽Array的變化,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

回憶

在上一篇Vue響應(yīng)式原理-理解Observer、Dep、Watcher簡單講解了Observer、Dep、Watcher三者的關(guān)系。

在Observer的偽代碼中我們模擬了如下代碼:

class Observer {
 constructor() {
  // 響應(yīng)式綁定數(shù)據(jù)通過方法
  observe(this.data);
 }
}

export function observe (data) {
 const keys = Object.keys(data);
 for (let i = 0; i < keys.length; i++) {
  // 將data中我們定義的每個屬性進行響應(yīng)式綁定
  defineReactive(obj, keys[i]);
 }
}

export function defineReactive () {
 // ...省略 Object.defineProperty get-set
}

今天我們就進一步了解Observer里還做了什么事。

Array的變化如何監(jiān)聽?

data 中的數(shù)據(jù)如果是一個數(shù)組怎么辦?我們發(fā)現(xiàn)Object.defineProperty對數(shù)組進行響應(yīng)式化是有缺陷的。

雖然我們可以監(jiān)聽到索引的改變。

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("數(shù)據(jù)被改變了,我要渲染到頁面上去!");
    }
  })
}

let data = [1];

// 對數(shù)組key進行監(jiān)聽
defineReactive(data, 0, 1);
console.log(data[0]); // 我被讀了,我要不要做點什么好?
data[0] = 2; // 數(shù)據(jù)被改變了,我要渲染到頁面上去!

但是defineProperty不能檢測到數(shù)組長度的變化,準確的說是通過改變length而增加的長度不能監(jiān)測到。這種情況無法觸發(fā)任何改變。

data.length = 0; // 控制臺沒有任何輸出

而且監(jiān)聽數(shù)組所有索引的的代價也比較高,綜合一些其他因素,Vue用了另一個方案來處理。

首先我們的observe需要改造一下,單獨加一個數(shù)組的處理。

// 將data中我們定義的每個屬性進行響應(yīng)式綁定
export function observe (data) {
  const keys = Object.keys(data);
  for (let i = 0; i < keys.length; i++) {
    // 如果是數(shù)組
    if (Array.isArray(keys[i])) {
      observeArray(keys[i]);
    } else {
      // 如果是對象
      defineReactive(obj, keys[i]);
    }
  }
}

// 數(shù)組的處理
export function observeArray () {
  // ...省略
}

那接下來我們就應(yīng)該考慮下Array變化如何監(jiān)聽?

Vue 中對這個數(shù)組問題的解決方案非常的簡單粗暴,就是對能夠改變數(shù)組的方法做了一些手腳。

我們知道,改變數(shù)組的方法有很多,舉個例子比如說push方法吧。push存在Array.prototype上的,如果我們能
能攔截到原型上的push方法,是不是就可以做一些事情呢?

Object.defineProperty

對象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符和存取描述符。存取描述符是由getter-setter函數(shù)對描述的屬性,也就是我們用來給對象做響應(yīng)式綁定的。Object.defineProperty-MDN

雖然我們無法使用Object.defineProperty將數(shù)組進行響應(yīng)式的處理,也就是getter-setter,但是還有其他的功能可以供我們使用。就是數(shù)據(jù)描述符,數(shù)據(jù)描述符是一個具有值的屬性,該值可能是可寫的,也可能不是可寫的。

value

該屬性對應(yīng)的值??梢允侨魏斡行У?JavaScript 值(數(shù)值,對象,函數(shù)等)。默認為 undefined。

writable

當且僅當該屬性的writable為true時,value才能被賦值運算符改變。默認為 false。

因此我們只要把原型上的方法,進行value的重新賦值。

如下代碼,在重新賦值的過程中,我們可以獲取到方法名和所有參數(shù)。

function def (obj, key) {
  Object.defineProperty(obj, key, {
    writable: true,
    enumerable: true,
    configurable: true,
    value: function(...args) {
      console.log('key', key);
      console.log('args', args); 
    }
  });
}

// 重寫的數(shù)組方法
let obj = {
  push() {}
}

// 數(shù)組方法的綁定
def(obj, 'push');

obj.push([1, 2], 7, 'hello!');
// 控制臺輸出 key push
// 控制臺輸出 args [Array(2), 7, "hello!"]

通過如上代碼我們就可以知道,用戶使用了數(shù)組上原型的方法以及參數(shù)我們都可以攔截到,這個攔截的過程就可以做一些變化的通知。

Vue監(jiān)聽Array三步曲

接下來,就看看Vue是如何實現(xiàn)的吧~

第一步:先獲取原生 Array 的原型方法,因為攔截后還是需要原生的方法幫我們實現(xiàn)數(shù)組的變化。

第二步:對 Array 的原型方法使用 Object.defineProperty 做一些攔截操作。

第三步:把需要被攔截的 Array 類型的數(shù)據(jù)原型指向改造后原型。

我們將代碼進行下改造,攔截的過程中還是要將開發(fā)者的參數(shù)傳給原生的方法,保證數(shù)組按照開發(fā)者的想法被改變,然后我們再去做視圖的更新等操作。

const arrayProto = Array.prototype // 獲取Array的原型

function def (obj, key) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    value: function(...args) {
      console.log(key); // 控制臺輸出 push
      console.log(args); // 控制臺輸出 [Array(2), 7, "hello!"]
      
      // 獲取原生的方法
      let original = arrayProto[key];
      // 將開發(fā)者的參數(shù)傳給原生的方法,保證數(shù)組按照開發(fā)者的想法被改變
      const result = original.apply(this, args);

      // do something 比如通知Vue視圖進行更新
      console.log('我的數(shù)據(jù)被改變了,視圖該更新啦');
      this.text = 'hello Vue';
      return result;
    }
  });
}

// 新的原型
let obj = {
  push() {}
}

// 重寫賦值
def(obj, 'push');

let arr = [0];

// 原型的指向重寫
arr.__proto__ = obj;

// 執(zhí)行push
arr.push([1, 2], 7, 'hello!');
console.log(arr);

被改變后的arr。

Vue源碼解析

array.js

Vue在array.js中重寫了methodsToPatch中七個方法,并將重寫后的原型暴露出去。

// Object.defineProperty的封裝
import { def } from '../util/index'

// 獲得原型上的方法
const arrayProto = Array.prototype

// Vue攔截的方法
const methodsToPatch = [
 'push',
 'pop',
 'shift',
 'unshift',
 'splice',
 'sort',
 'reverse'
];

// 將上面的方法重寫
methodsToPatch.forEach(function (method) {
  def(arrayMethods, method, function mutator (...args) {
    console.log('method', method); // 獲取方法
    console.log('args', args); // 獲取參數(shù)

   // ...功能如上述,監(jiān)聽到某個方法執(zhí)行后,做一些對應(yīng)的操作
    // 1、將開發(fā)者的參數(shù)傳給原生的方法,保證數(shù)組按照開發(fā)者的想法被改變
    // 2、視圖更新等
  })
})

export const arrayMethods = Object.create(arrayProto);

observer

在進行數(shù)據(jù)observer綁定的時候,我們先判斷是否hasProto,如果存在__proto__,就直接將value 的 __proto__指向重寫過后的原型。如果不能使用 __proto__,貌似有些瀏覽器廠商沒有實現(xiàn)。那就直接循環(huán) arrayMethods把它身上的這些方法直接裝到 value 身上好了。畢竟調(diào)用某個方法是先去自身查找,當自身找不到這關(guān)方法的時候,才去原型上查找。

// 判斷是否有__proto__,因為部分瀏覽器是沒有__proto__
const hasProto = '__proto__' in {}
// 重寫后的原型
import { arrayMethods } from './array'
// 方法名
const arrayKeys = Object.getOwnPropertyNames(arrayMethods);

// 數(shù)組的處理
export function observeArray (value) {
  // 如果有__proto__,直接覆蓋        
  if (hasProto) {
    protoAugment(value, arrayMethods);
  } else {
    // 沒有__proto__就把方法加到屬性自身上
    copyAugment(value, arrayMethods, )
  }
}

// 原型的賦值
function protoAugment (target, src) {
  target.__proto__ = src;
}

// 復(fù)制
function copyAugment (target, src, keys) {
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i]
    def(target, key, src[key]);
  }
}

通過上面的代碼我們發(fā)現(xiàn),沒有直接修改 Array.prototype,而是直接把 arrayMenthods 賦值給 value 的 __proto__ 。因為這樣不會污染全局的Array, arrayMenthods 只對 data中的Array 生效。

總結(jié)

因為監(jiān)聽的數(shù)組帶來的代價和一些問題,Vue使用了重寫原型的方案代替。攔截了數(shù)組的一些方法,在這個過程中再去做通知變化等操作。

本文的一些代碼均是Vue源碼簡化后的,為了方便大家理解。思想理解了,源碼就容易看懂了。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • vue服務(wù)端渲染添加緩存的方法

    vue服務(wù)端渲染添加緩存的方法

    vue服務(wù)區(qū)緩存分為頁面緩存、組建緩存和接口緩存,本文通過實例代碼逐一給大家介紹,本文重點給大家介紹vue服務(wù)端渲染添加緩存的方法,感興趣的朋友跟隨小編一起看看吧
    2018-09-09
  • 在Linux服務(wù)器上部署vue項目

    在Linux服務(wù)器上部署vue項目

    這篇文章介紹了在Linux服務(wù)器上部署vue項目的方法,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2021-11-11
  • vue組件之間進行傳值的方法

    vue組件之間進行傳值的方法

    這篇文章主要介紹了vue組件之間進行傳值的方法,文章圍繞主題展開詳細的內(nèi)容戒殺,具有一定的參考價值,需要的朋友可以參考一下
    2022-09-09
  • vue通過獲取url中的信息登錄頁面的代碼詳解

    vue通過獲取url中的信息登錄頁面的代碼詳解

    這篇文章主要給大家介紹了vue通過獲取url中的信息登錄頁面的方法,文中通過代碼示例給大家介紹的非常詳細,具有一定的參考價值,需要的朋友可以參考下
    2024-02-02
  • vue中如何實現(xiàn)變量和字符串拼接

    vue中如何實現(xiàn)變量和字符串拼接

    這篇文章主要介紹了vue中如何實現(xiàn)變量和字符串拼接,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • 深入理解vue-loader如何使用

    深入理解vue-loader如何使用

    本篇文章主要介紹了vue-loader,vue-loader是webpack下loader插件 可以把.vue文件輸出成組件,有興趣的可以了解一下
    2017-06-06
  • vue3自定義指令自動獲取節(jié)點的width和height代碼示例

    vue3自定義指令自動獲取節(jié)點的width和height代碼示例

    這篇文章主要介紹了如何使用ResizeObserver監(jiān)聽組件的寬度和高度,并將其封裝成一個指令以便全局或局部使用,ResizeObserver可以監(jiān)聽元素的多個尺寸屬性,如top、bottom、left等,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-11-11
  • 淺談vuex為什么不建議在action中修改state

    淺談vuex為什么不建議在action中修改state

    這篇文章主要介紹了淺談vuex為什么不建議在action中修改state,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧
    2020-02-02
  • 基于IView中on-change屬性的使用詳解

    基于IView中on-change屬性的使用詳解

    下面小編就為大家分享一篇基于IView中on-change屬性的使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • Vue自定義指令詳解

    Vue自定義指令詳解

    這篇文章主要為大家介紹了Vue自定義指令,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11

最新評論

江源县| 朝阳县| 阿拉尔市| 新乐市| 黄骅市| 临汾市| 绥中县| 万荣县| 白山市| 嫩江县| 桐庐县| 鲜城| 眉山市| 昭觉县| 南和县| 迁安市| 永胜县| 鄯善县| 手机| 孟连| 伊金霍洛旗| 大兴区| 汕头市| 文安县| 苍梧县| 黄骅市| 潢川县| 新平| 德安县| 泸水县| 泸州市| 文化| 平塘县| 宜都市| 江陵县| 平凉市| 蓝山县| 邯郸县| 巨野县| 中江县| 台南市|