1. 程式人生 > 程式設計 >vue2.x陣列劫持原理的實現

vue2.x陣列劫持原理的實現

接上篇Vue2.x 物件劫持,繼續來寫陣列劫持

實現原理:
1 重新定義原生陣列方法push unshift shift pop splice sort reverse 因為這些方法可以修改原陣列。
2 拿到原生陣列方法 Object.create(Array.prototype)
3 AOP攔截,再執行重寫陣列方法前,先執行原生陣列方法

核心監聽Observer程式碼

// 把data中的資料 都使用Object.defineProperty重新定義 es5
// Object.defineProperty 不能相容ie8 及以下 vue2 無法相容ie8版本
import {arrayMethods} from './array.js'
import {
  isObject,def
} from '../util/index'
// 後續我可以知道它是不是一個已經觀察了的資料 __ob__
class Observer{
  constructor(value){ // 僅僅是初始化的操作
    // vue如果資料的層次過多 需要遞迴的去解析物件中的屬性,依次增加set和get方法
    // value.__ob__ = this; // 我給每一個監控過的物件都增加一個__ob__屬性
    def(value,'__ob__',this);
    if(Array.isArray(value)){
      // 如果是陣列的話並不會對索引進行觀測 因為會導致效能問題
      // 前端開發中很少很少 去操作索引 push shift unshift 
      value.__proto__ = arrayMethods;
      // 如果數組裡放的是物件我再監控
      this.observerArray(value);
    }else{
       // 對陣列監控
      this.walk(value); // 對物件進行觀測
    }
  }
  observerArray(value){ // [{}]
    for(let i = 0; i < value.length;i++){
      observe(value[i]);
    }
  }
  walk(data){
    let keys = Object.keys(data); // [name,age,address]
    // 如果這個data 不可配置 直接reurn
    keys.forEach((key)=>{
      defineReactive(data,key,data[key]);
    });
  }
}
function defineReactive(data,value){

  observe(value); // 遞迴實現深度檢測
  Object.defineProperty(data,{
    configurable:true,enumerable:false,get(){ // 獲取值的時候做一些操作
      return value;
    },set(newValue){ // 也可以做一些操作
      console.log('更新資料')
      if(newValue === value) return;
      observe(newValue); // 繼續劫持使用者設定的值,因為有可能使用者設定的值是一個物件
      value = newValue;
    }
  });
}

export function observe(data) {
  let isObj = isObject(data);
  if (!isObj) {
    return
  }
  return new Observer(data); // 用來觀測資料
}

重寫原生陣列方法

// 我要重寫陣列的那些方法 7個 push shift unshift pop reverse sort splice 會導致陣列本身發生變化
// slice()


let oldArrayMethods = Array.prototype;
// value.__proto__ = arrayMethods 原型鏈查詢的問題, 會向上查詢,先查詢我重寫的,重寫的沒有會繼續向上查詢
// arrayMethods.__proto__ = oldArrayMethods
export const arrayMethods = Object.create(oldArrayMethods); 

const methods = [
  'push','shift','unshift','pop','sort','splice','reverse'
]
methods.forEach(method=>{
  arrayMethods[method] = function (...args) { 
    const result = oldArrayMethods[method].apply(this,args); // 呼叫原生的陣列方法
    // push unshift 新增的元素可能還是一個物件
    let inserted; // 當前使用者插入的元素
    let ob = this.__ob__;
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args;
        break;
      case 'splice': // 3個 新增的屬性 splice 有刪除 新增的的功能 arr.splice(0,1,{name:1})
        inserted = args.slice(2)
      default:
        break;
    }
    if(inserted) ob.observerArray(inserted); // 將新增屬性繼續觀測


    return result;
  }
})

工具方法定義如下:

/**
 * 
 * @param {*} data 當前資料是不是物件
 */
export function isObject(data) {
  return typeof data === 'object' && data !== null;
}
export function def(data,value){
  Object.defineProperty(data,{
    enumerable:false,configurable:false,value
  })
}

到此這篇關於vue2.x陣列劫持原理的實現的文章就介紹到這了,更多相關vue2.x 陣列劫持內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!