The book follows: juejin.cn/post/691414…

Problem 1.

Vue2. X hijacks getter and setter operations for data data with Object.defineProperty. In this way, the function of detecting changes in the data in response is implemented. However, Object.defineProperty has some natural drawbacks, including:

1. The getter and setter of object.defineProperty can only detect whether a data is modified or not, but cannot track the deletion and addition of attributes, so the new and deleted attributes in the Object cannot be detected. For example, deleteProperty can hijack the delete operation.)

2. In the Observer implementation of Object change detection above:


for(let i =0; i<keys.length; i++){ defineReactive(obj,keys[i],obj[keys[i]]) }Copy the code

Setting the getter/setter for each property of an object by traversing it. (Feels wrong, but lacks the adjective).

Object.defineproperty has a built-in defect that can’t listen for array changes. Vue2. X uses its own method to override the array prototype, so we can capture push, pop, slice and other operations, but there are still problems, such as: GplArray [0]=1, such an operation, can not capture data changes.

2.proxy

Overview: Proxy is used to modify the default behavior of certain operations, which is equivalent to making changes at the language level, so it is a kind of “metaprogramming”, that is, programming in a programming language. How to use, for example:

var obj = new Proxy({}, {
  get: function (target, key, receiver) {
    console.log(`getting ${key}! `);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, value, receiver) {
    console.log(`setting ${key}! `);
    return Reflect.set(target, key, value, receiver); }}); obj.gpl =1
// setting gpl!
++obj.gpl
// getting gpl!
// setting gpl!
//  2
Copy the code

DefineReactive encapsulates Object.defineProperty and defines a reactive data. Once wrapped, get is fired whenever data is read from data’s key, and set is fired whenever data is set into data’s key.

3. Use proxy to try to implement the above code again

To sum up, and Object change detection with Object. DefineProperty implementation of the old code, an attempt to Object change detection proxy way to rewrite.

//Observer
export class Observer {
    constructor (value) {
      this.value = value
      if(!Array.isArray(value)){
        this.walk(value)
      }
    }
    walk(obj) {
      const keys = Object.keys(obj)
      for(let i =0; i<keys.length; i++){ defineReactive(obj,keys[i],obj[keys[i]]) } } }/ / Object. DefineProperty encapsulation
  function defineReactive(data,key,val) {
    if(typeof val === 'object') {new Observer(val)
    }
    let dep = new Dep();
    Object.defineProperty(data,key,{
      enumerable:true.configurable:true.get:function(){
        dep.depend()
        return val;
      },
      set:function(newVal){
        if(val === newVal){
          return; } val = newVal; dep.notify(); }})}//Dep
 export default class Dep {
  constructor(){
    this.subs = []
  }
  addSub(sub){
    this.subs.push(sub)
  }
  removeSub(sub){
    remove(this.subs,sub)
  }
  depend(){
    if(window.target){
      this.addSub(window.target)
    }
  }
  notify(){
    const subs = this.subs.slice()
    for(let i =0; i<subs.length; i++){ subs[i].update() } } }function remove(arr,item) {
    if(arr.length){
      const index = arr.indexOf(item);
      if(index > -1) {return arr.splice(index,1)}}}Copy the code

Rewrite:

[key:[wather,…]],… In the form of

export default class Dep {
    constructor(){
        this.subs = new Map(a); }addSub(key,watcher){
        let watcherList = this.subs.get(key)||[];
        if(! watcherList.includes(watcher)){ watcherList.push(watcher);this.subs.set(key,watcher); }}removeSub(key){
        this.subs.delete(key)
    }
    depend(key){
        if(window.target){
            this.addSub(key,window.target)
        }
    }
    notify(key){
        let watcherList = this.subs.get(key);
        for(let watcher of watcherList){
            watcher.update()
        }
    }
}
 
 
export class Observer {
    constructor (value) {
        this.value = this.defineReactive(value)
    }
    static defineReactive(data){
        let dep = new Dep();
        return new Proxy(data,{
            get(obj, key) {
                dep.depend(key)
                return Reflect.get(obj, key);
            },
            set(obj, key, newVal) {
                Reflect.set(obj, key, newVal);
                dep.notify(key)
            }
        })
    }
}
// Shanzhai ultra low Vue
class Vue {
    constructor({data}) {
        this.$data = Observer.defineReactive(data()); }}let vm = new Vue({
    data: () = >({xxx:'gpl'})});` ``

Copy the code