美文网首页web前端高级-vue
手动实现vue中的watch监听

手动实现vue中的watch监听

作者: 老鼠AI大米_Java全栈 | 来源:发表于2019-04-26 12:16 被阅读0次

    vue中的双向数据绑定,数据监听用起来很方便,其实也可以自己实现一个简单的监听功能,一起来看看

    getter和setter

    getter 是一种获得属性值的方法,setter是一种设置属性值的方法。
    属性被赋值 a = 1的时候, a 的原型内的setter就会被触发;
    而 console.log(a) 的时候,a 的原型内的getter就会被触发。

    这里选用了比较好构造的一种 Object.defineProperty

    概要
    Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。
    语法
    Object.defineProperty(obj, prop, descriptor)
    参数
    obj 需要定义属性的对象。
    prop 需被定义或修改的属性名。
    descriptor 需被定义或修改的属性的描述符。

    如下面一个实例,定义一个新的属性

    (function () {
      var o = { a : 1}//声明一个对象,包含一个 a 属性,值为1
      Object.defineProperty(o,"b",{
        get: function () {
          return this.a;
        },
        set : function (val) {
          this.a = val;
        },
        configurable : true
      });
      console.log(o.b);//==> 1
      o.b = 2;
      console.log(o.b);//==> 2
    })();
    

    configurable是指 "b" 是否可以被再配置,默认是false。
    false的话Object.defineProperty(o,"a",{set : function(val){}} );
    再修改时会不起作用或者报错,一般默认false。

    创建Watch对象

    目标实现,以下是我们想要的达到的效果

    import watcher from './watcher.js';
    let wm = new watcher({
      data:{
        a: 0 
      },
      watch:{
        a(newVal,oldVal){
          console.log('newVal:'+newVal);
          console.log('oldVal:'+oldVal);
        }
      }
    })
    vm.a = 1 
    // newVal:1
    // oldVal:0
    

    创建构造对象

    class watcher{
      constructor(opts){
        this.$data = opts.data;
        for(let key in opts.data){
          this.setData(key,opts.data[key])
        }
      }
     
      setData(_key,_val){
        Object.defineProperty(this,_key,{
          get: function () {
            return this.$data[_key];
          },
          set : function (val) {
            const oldVal = this.$data[_key];
            if(oldVal === val)return val;
            this.$data[_key] = val;
            return val;
          },
        });
      }
    }
     
    export default watcher;
    

    该对象只是实现了简单的setter和getter,并没有实现对应的监听函数

    添加watch事件触发

    class watcher{
      constructor(opts){
        this.$data = this.getBaseType(opts.data) === 'Object' ? opts.data : {};
        this.$watch = this.getBaseType(opts.watch) === 'Object' ? opts.watch : {};
        for(let key in opts.data){
          this.setData(key)
        }
      }
     
      getBaseType(target) {
        const typeStr = Object.prototype.toString.apply(target);
       
        return typeStr.slice(8, -1);
      }
     
      setData(_key){
        Object.defineProperty(this,_key,{
          get: function () {
            return this.$data[_key];
          },
          set : function (val) {
            const oldVal = this.$data[_key];
            if(oldVal === val)return val;
            this.$data[_key] = val;
            this.$watch[_key] && typeof this.$watch[_key] === 'function' && (
              this.$watch[_key].call(this,val,oldVal)
            );
            return val;
          },
        });
      }
    }
     
    export default watcher;
    

    为了函数内部的健壮性,getBaseType是用来做类型校验的。
    Object.defineProperty(this),this把上下文指向当前对象。
    this.$watch[_key].call(this,val,oldVal),把监听事件的上下文页绑定到当前对象,方便在watch内通过this获取对象内的值,如下

    let wm = new watcher({
      data:{
        a: 0,
        b: 'hello'
      },
      watch:{
        a(newVal,oldVal){
          console.log(this.b);
        }
      }
    })
    

    有人可能会问为什么不直接用vue呢。你也知道vue是一个工程级别的框架,做比较大的项目当然是用vue,react;但是单单做一个展示性的官网或者做个移动端的H5宣传页也用上vue吗?那当然是没有必要的。
    用上这一个watcher类,可以让你页面的状态控制有条理、有迹可循。

    相关文章

      网友评论

        本文标题:手动实现vue中的watch监听

        本文链接:https://www.haomeiwen.com/subject/pajfnqtx.html