vue数据监听和劫持

作者: wade3po | 来源:发表于2020-04-08 16:36 被阅读0次

    vue对数据的监听都知道是通过Object.defineProperty,今天简单把过程捋了一下,弄清楚vue对数据的处理。

    //index.js
    //声明vue构造函数,进行初始化
    function Vue(options) {
     this._init(options);
    }
    
    Vue.prototype._init = function (options) {
     //定义一个vm
     let vm = this;
     vm.$options = options;
     //初始化
     initState(vm);
    }
    
    //observe/index.js
    function initState(vm) {
     //初始化props methods data computed watch
     let opts = vm.$options;
     if(opts.data){
      initData(vm);
     }
     // if(opts.watch){
     //   initWatch(opts.watch);
     // }
    }
    
    function initData(vm) {
     let data = vm.$options.data;
     //挂载data到vm_data,不改变原data
     data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};
     //代理data上的数据到_data
     for (let key in data) {
      proxy(vm, '_data', key)
     }
     //数据进行观察
     observe(vm._data);
    }
    
    function proxy(vm, source, key) {
     Object.defineProperty(vm, key, {
      get(){
       return vm[source][key];
      },
      set(newValue){
       vm[source][key] = newValue;
      }
     })
    }
    
    function observe(data) {
     //如果不是对象,就不进行监听{a:5},监听了a,不监听5
     if(typeof data !== 'object' || data === null){
      return
     }
     return new Observer(data);
    }
    
    //observe/observer.js
    function Observer(data) {
     //如果是数组另外处理
     if(Array.isArray(data)){
      //劫持数组方法
      data.__proto__ = arrrayMethods;
      //数组每一项进行监听
      observerArray(data);
     }else{
      //对象进行监听
      this.walk(data);
     }
    }
    Observer.prototype.walk = function (data) {
     let keys = Object.keys(data);
     for (let i = 0; i < keys.length; i++) {
      defineReactive(data, keys[i], data[keys[i]]);
     }
    }
    
    //核心代码,对对象进行监听
    function defineReactive(data, key, value) {
     //判断,如果对象里面嵌套对象,递归监听
     //vue一个性能痛点,vue3用proxy改进,proxy兼容会有点问题
     observe(value);
     //核心
     Object.defineProperty(data, key, {
      get(){
       console.log('get---' + key + '---' + value);
       return value;
      },
      set(newValue){
       console.log('set---' + key + '---' + value);
       if(value === newValue){
        return
       }
       value = newValue;
      }
     })
    }
    
    //observe/array.js
    let arrayProto = Array.prototype;
    //拷贝数组方法
    let arrrayMethods = Object.create(arrayProto);
    let changeMethods = ['push', 'pop', 'unshift', 'shift', 'sort', 'reverse', 'splice'];
    changeMethods.forEach(method => {
     //对七个会改变原数组的方法进行劫持,切片编程
     arrrayMethods[method] = function (...args) {
      let inserted;
      switch (method) {
       case 'push':
       case 'unshift':
        inserted = args;
        break;
       case 'splice':
        inserted = args.splice(2);
        break;
       default:
        break;
      };
      //对于新增的数组项进行监听
      if(inserted) observerArray(inserted);
      //最终还是调用数组原方法
      return arrayProto[method].apply(this, args);
     }
    })
    
    function observerArray(inserted) {
     for (let i = 0; i < inserted.length; i++) {
      observe(inserted[i]);
     }
    }
    

    然后就可以去调用:

    let vm = new Vue({
     data(){
      return {
       msg: 'hello',
       obj: {a: 10},
       arr: [1, 2, 3]
      }
     },
     methods:{},
    })
    
    console.log(vm.$options);
    

    这边只是很简单的理了一下,源码大概也能找到这些方法,源码当然更复杂,把方法都写到一起了,如果是npm包的话是通过import和export导入导出,这边也有把划分的写了一下。

    真的觉得好好理解一下,对于vue的数据响应有很大帮助,复制到编辑器里,静下来理一理,你会发现顿悟一样。

    image

    相关文章

      网友评论

        本文标题:vue数据监听和劫持

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