ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

手摸手教你实现一个简单vue(2)上手编写observer

2022-02-03 10:34:11  阅读:181  来源: 互联网

标签:vue obj observer parent dep value key 手教 property


事前哔哔

上一节我们讲了响应式原理,比较笼统又难以理解,所以这一节我们就直接上手开始编写,在章节末尾我把observer对象转换成了js版本供你直接上手测试,你可以先copy边调试边理解接下来的编写流程

开始

observer的侦测是一个对象或者数组,所以一开始我们便要传递这个参数(取名为value)进来

 class ObserverNext {
  $value: any;
  constructor(value) {
  ...
  }

在前面响应式原理中我们说了我们要深度递归分析这个对象(就是data选项)内的数据所以我们还得有个walk方法

 class ObserverNext {
  $value: any;
  constructor(value) {
  ... 
  this.walk(value)
  }
  private walk(obj: Object | Array<any>) {
    for (const [key, val] of Object.entries(obj)) {
      if (typeof val == "object") {
        //同时判断数组和对象
        new ObserverNext(key,val, obj);
      }
    }
  }

现在就完成了深度递归,但是还没有分析的过程,所以我们得再写一个方法,名为detect来分析,这里我们用Proxy完成对该对象的监控(分析)

//observer中的detect私有方法
 private detect(val: any, parent: any) {
    const dep = this.dep//在constructor中定义
    const proxy = new Proxy(val, {
      get(obj, property) {
        if (!obj.hasOwnProperty(property)) {
          return;
        }
        dep.depend(property);
        return obj[property];
      },
      set(obj, property, value) {
        obj[property] = value;
        dep.notify(property);
        return true;
      },
    });

  }

当对象被get了数据,我们就收集依赖,当对象set了数据,我们就通知依赖更新,
并且呢,这个proxy要被外部能够获取到,这里我们的解决方案是直接在父对象上替换

   parent[key] = proxy;

所以我们一开始就要传递父对象(parent)和键(key)进来。
所以我们的constructor就变成了

 constructor(key,value, parent) {
    this.$key=key;
    this.$value = value;

    this.$parent = parent;
    this.dep = new Dep();
    
    this.walk(value);
    this.detect(value, parent);
  }

现在我们的observer变成了

class ObserverNext {
  $value: any;
  $parent: any;
  $key:string
  dep: any;
  constructor(key,value, parent) {
    this.$key=key;
    this.$value = value;

    this.$parent = parent;

    this.dep = new Dep();

    //def(value, "__ob__", this);
    this.walk(value);
    this.detect(value, parent);
  }
  private walk(obj: Object | Array<any>) {
    for (const [key, val] of Object.entries(obj)) {
      if (typeof val == "object") {
        //同时判断数组和对象
        new ObserverNext(key,val, obj);
      }
    }
  }
  private detect(val: any, parent: any) {
    const dep = this.dep
    const key=this.$key
    const proxy = new Proxy(val, {
      get(obj, property) {
        if (!obj.hasOwnProperty(property)) {
          return;
        }
        dep.depend(property);
        return obj[property];
      },
      set(obj, property, value) {
        obj[property] = value;

        dep.notify(property);
        //if(parent.__ob__)parent.__ob__.dep.notify(key)

        return true;
      },
    });

    parent[key] = proxy;
  }
}

但是现在有个bug,如果儿子对象更新了,它只会通知自己的依赖收集器(dep)更新,父对象不会感知到任何异常!

所以我们得通知父对象的观察者类实例(ObserverNext)更新
因此,在每个对象中我们都要记录一下观察者实例
大概如下:

constructor(key,value,parent){
...
def(value, "__ob__", this); 
//相当于value.__ob__=this;
...
}

然后在proxy的set中,我们要通知父对象更新于是

set(){
if(parent.__ob__)parent.__ob__.dep.notify(key)
}

最终成果

我们原版是typescript版本的,但为了方便各位直接丢控制台里测试,我转换成了js版本以供各位调试

class Dep {
  constructor() {}
  depend() {
    console.log("依赖收集");
  }
  notify() {
    console.log("依赖更新");
  }
}
function def(obj, key, val, enumerable=false) {
    Object.defineProperty(obj, key, {
      value: val,
      enumerable: !!enumerable,
      writable: true,
      configurable: true,
    });
  }
class ObserverNext {
  constructor(key, value, parent) {
    this.$key = key;
    this.$value = value;

    this.$parent = parent;

    this.dep = new Dep();
   
    def(value, "__ob__", this);
    this.walk(value);
    this.detect(value, parent);
  }
  walk(obj) {
    for (const [key, val] of Object.entries(obj)) {
      if (typeof val == "object") {
        //同时判断数组和对象
        new ObserverNext(key, val, obj);
      }
    }
  }
  detect(val, parent) {
    const dep = this.dep;
    const key = this.$key;
    const proxy = new Proxy(val, {
      get(obj, property) {
        if (!obj.hasOwnProperty(property)) {
          return;
        }
        dep.depend(property);
        return obj[property];
      },
      set(obj, property, value) {
        obj[property] = value;

        dep.notify(property);
        if (parent.__ob__) parent.__ob__.dep.notify(key);

        return true;
      },
    });

    parent[key] = proxy;
  }
}

const vm = {
  data: {
    attr1: {
      a: 1,
      b: 2,
      c: 3,
    },
    array: [1, 2, 3],
  },
};

new ObserverNext('data',vm.data,vm);
//测试
//vm.data.attr1 ->return 依赖收集

归档

# 手摸手教你实现一个简单vue(1)响应式原理
# 手摸手教你实现一个简单vue(2)上手编写observer
# 手摸手教你实现一个简单vue(3)上手编写dep和watcher

标签:vue,obj,observer,parent,dep,value,key,手教,property
来源: https://www.cnblogs.com/moushicheng/p/15861227.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有