1. 程式人生 > 實用技巧 >vue原始碼解析data與watcher

vue原始碼解析data與watcher

以一份簡單的例子說明:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src='node_modules/vue/dist/vue.js' type='text/javascript'></script>

</head>
<body>
<div id='app'></div>
<script type='text/javascript'>
    var
vueIns=new Vue({ data(){ return { aaa:1, bbb:2 } }, created(){ this.aaa =3 }, methods:{ confirm(){ this.bbb=333 } }, template:'<div><h1>{{aaa}}</h1><h2>{{bbb}}</h2></div>' }).$mount(
'#app') console.log(vueIns) </script> </body> </html>

Vue物件中的data物件

Observer.prototype.walk = function walk (obj) {
    var keys = Object.keys(obj);
    for (var i = 0; i < keys.length; i++) {
      defineReactive$$1(obj, keys[i]);
    }
};

/**
 * Define a reactive property on an Object.
 
*/ function defineReactive$$1 ( obj, key, val, customSetter, shallow ) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return } // cater for pre-defined getter/setters var getter = property && property.get; var setter = property && property.set; if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } var childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (customSetter) { customSetter(); } // #7981: for accessor properties without setter if (getter && !setter) { return } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } }); }

將_data的資料進行遍歷defineProperty

得到:vm.aaa的形式

_data:
   aaa: 3
   bbb: 2
   __ob__: Observer {value: {…}, dep: Dep, vmCount: 1}
   get aaa: ƒ reactiveGetter()
   set aaa: ƒ reactiveSetter(newVal)
   get bbb: ƒ reactiveGetter()
   set bbb: ƒ reactiveSetter(newVal)

代理:proxy方法

proxy(vm, "_data", key); 

function proxy (target, sourceKey, key) {
    sharedPropertyDefinition.get = function proxyGetter () {
      return this[sourceKey][key]
    };
    sharedPropertyDefinition.set = function proxySetter (val) {
      this[sourceKey][key] = val;
    };
    Object.defineProperty(target, key, sharedPropertyDefinition);
  }

  得到

aaa: 3
bbb: 2

__proto__: Object
   get aaa: ƒ proxyGetter()
   set aaa: ƒ proxySetter(val)
   get bbb: ƒ proxyGetter()
   set bbb: ƒ proxySetter(val)

  

資料監控執行

initWatch

 function initWatch (vm, watch) {
    for (var key in watch) {
      var handler = watch[key];
      if (Array.isArray(handler)) {
        for (var i = 0; i < handler.length; i++) {
          createWatcher(vm, key, handler[i]);
        }
      } else {
        createWatcher(vm, key, handler);
      }
    }
  }

  function createWatcher (
    vm,
    expOrFn,
    handler,
    options
  ) {
    if (isPlainObject(handler)) {
      options = handler;
      handler = handler.handler;
    }
    if (typeof handler === 'string') {
      handler = vm[handler];
    }
    return vm.$watch(expOrFn, handler, options)
  }


   Vue.prototype.$watch = function (
      expOrFn,
      cb,
      options
    ) {
      var vm = this;
      if (isPlainObject(cb)) {
        return createWatcher(vm, expOrFn, cb, options)
      }
      options = options || {};
      options.user = true;
      var watcher = new Watcher(vm, expOrFn, cb, options);
      if (options.immediate) {
        try {
          cb.call(vm, watcher.value);
        } catch (error) {
          handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
        }
      }
      return function unwatchFn () {
        watcher.teardown();
      }
    };
  }