vue2.x(data&watcher&dep)

如何理解vue2.x中的响应式数据?

    1. 执行initData方法
    2.                -> 作“data” & “props” & “methods”属性名称是否重名判断 且调用proxy方法再作this.data proxy代理到 this._data上面
    3.                -> 调用observe全局监听函数 准备作响应式处理
    4. 执行observe方法
    5.               -> 通过__ob__判断是否已经作了监听 (若未进行监听 则实例化Observer来进行监听创建)
    6. 执行new Observer() 实例化 (我们可以叫他为大目标者)
    7.                         -> 这里需要注意的是这个Observer监听者是针对该组件下整个data进行存储 data -> this.value & this.dep = new Dep() 
                                  并对该value值(data)上增加__ob__ 指向当前Observer 等于说就是 data上有个__ob__隐藏属性
    8. 最后执行Observer大目标者原型上walk方法
    9.                                    -> for循环遍历调用defineReactive$$1方法来进行data上每个属性进行响应式设置
    10. 给每个属性执行new Dep() 实例化 (我们可以叫他为小监听者或实际目标收集者)若存在嵌套则重复调用observe方法
                          -> 其实这个dep才是真正的观察者模式中的叫做依赖收集者或者叫目标 因为dep.subs上会收集相关的监听者
                          -> 再使用Object.defineProperty定义每个data里面的key值的getter&setter 这里也利用了闭包特性缓存了每个key所对应的dep对象信息
                          -> 当getter时 判断全局下是否有Watcher 若有则调用当前dep.depend -> 再调用Watcher的addDep方法收集当前watcher到Watcher实例上的newDepId&newDep
                             针对Set格式的newDepId进行排除重复使用的data数据 比如我在多个地方同时使用data下数据
                          -> 当setter时 调用当前dep下的notify方法 再循环执行subs(Watcher)的update方法
    11. 将当前Watcher压入queue队列(这里压进队列是为了比如一个数据的修改可能对应多个地方的应用 采用收集完所有的更改变动后 一次性触发每个的生命钩子函数)
    12. 调用nextTick
    13. 触发‘beforeUpdate’钩子
    14. 调用Watcher的run方法
    15. 调用Watcher的get方法
                           -> 设置当前Watcher实例为全局Target
                           -> 调用Wathcer的getter方法 (一般情况下就是updateComponent方法)
                           -> 执行vm._update(vm._render(),hydrating)代码
    16. 调用render方法 (而这里的render其实就是经过编译过后形成的with包裹的匿名函数)
    17. 一旦执行with代码 则会触发属性的getter方法 (获取最新的值) 最后返回最新执行下的vnode (执行render方法后 获得新的vnode)
    18. 调用vm._update (这里就是走对比新老vnode 走diff算法 更新DOM)
    19. 将全局Target置为空
    20. 清空当前Watcher上面的newDepIds&newDep 等待下一次的更新                                           
    21. 若存在activedQueue 则触发钩子 'activated' 生命周期钩子函数
    22. 执行更新updateQueue队列 则触发每个vm实例下的 ‘update’ 生命周期钩子函数

要讲响应式我们只有从data上入手 先本地起一个简易vue

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #app span{
      color:red;
    }
  </style>
</head>
<body>
  <div id="app">
    <span>我是data数据{{message}}</span>
    <!-- <span>我是data数据{{list.name}}</span> -->
    <!-- <base-show /> -->
    <!-- <span>我是一模一样的data数据{{message}}</span> -->
    <p>我是数据依赖message{{`${message}依赖数据`}}</p>
  </div>
</body>
<script src="./vue.dist.js"></script>
<script>
  const vm = new Vue({
    data:{
      message:'test',
    },
    // computed:{
    //   getDep(){
    //     return this.message + '依赖数据'
    //   }
    // }
  })
  // 改变data值测试
  setTimeout(function(){
    vm.message = '2秒后变值'
  },2000)
  // 注册组件测试
  // Vue.component('base-show',{
  //   name:'baseShow',
  //   template:`<h3>{{myMessage}}</h3>`,
  //   data(){
  //     return {
  //       myMessage:'我是baseShow组件'
  //     }
  //   }
  // })
  vm.$mount('#app')
</script>
</html>
源码4998行 生命周期钩子第一个“beforeCreate”
源码5000执行initState(vm) 说明在beforeCreate钩子里面时拿不到被处理过的data
源码4635行 按顺序处理props -> methods -> data -> computed -> watch
function initState(vm) {
  vm._watchers = [];
  var opts = vm.$options;
  // TODO:vue先处理props -> methods -> data -> computed -> watch
  if (opts.props) {
    initProps(vm, opts.props);
  }
  if (opts.methods) {
    initMethods(vm, opts.methods);
  }
  if (opts.data) {
    // TODO:开始初始化data 传入vue实例
    initData(vm);
  } else {
    // TODO: 万一没设置 一般根点没设置data数据 这里容错处理赋值了个空{}
    // 后面来看这个oberve函数到底做了哪些事情
    observe((vm._data = {}), true /* asRootData */);
  }
  if (opts.computed) {
    initComputed(vm, opts.computed);
  }
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch);
  }
}
源码4699行 初始化data数据 其实此时这个时候的data已被处理
源码4983行 mergeOptions 把非组件类属性进行进一步处理
data = function mergedInstanceDataFn () {
    // instance merge
    var instanceData = typeof childVal === 'function'
    ? childVal.call(vm, vm)
    : childVal;
    var defaultData = typeof parentVal === 'function'
    ? parentVal.call(vm, vm)
    : parentVal;
    if (instanceData) {
        return mergeData(instanceData, defaultData)
    } else {
        return defaultData
    }
}
// 0.1 初始化data
function initData(vm) {
    var data = vm.$options.data;
    // TODO: 这里来一个判断 根结点data可以是一个对象
    // 而子组件data为函数 而且调用了全局getData 入参data及vue实例 来看0.1.5 getData意义
    // 这里在执行getData后 将我们声明的数据对象赋值给了vm实例中的_data属性及局部data变量
    data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {};
    if (!isPlainObject(data)) {
        data = {};
        warn('data functions should return an object:\n' + 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm);
    }
    // proxy data on instance
    // TODO:拿到声明data的key值
    var keys = Object.keys(data);
    // TODO: 这里拿的props及methods都是初始化数据未修饰
    var props = vm.$options.props;
    var methods = vm.$options.methods;
    var i = keys.length;
    // TODO: 发现挺多处使用while循环  学到了 嘻嘻
    while (i--) {
        var key = keys[i];
        // TODO: 这里就是作一个data跟props及methods是否重名判断
        // 为什么只判断这2个 我觉得是在0处 执行的顺序相关 先处理prop再methods 再data
        {
            if (methods && hasOwn(methods, key)) {
                warn('Method "' + key + '" has already been defined as a data property.', vm);
            }
        }
        if (props && hasOwn(props, key)) {
            warn('The data property "' + key + '" is already declared as a prop. ' + 'Use prop default value instead.', vm);
            // TODO:这里再判断下key 见0.1.1解析
        } else if (!isReserved(key)) {
            // TODO: 这里开始作一层_data的代理
            proxy(vm, '_data', key);
        }
    }
    // observe data
    // TODO: 这里调用全局observe函数 开始监听data
    observe(data, true /* asRootData */);
}
// 0.1.1 判断是否使用以特殊字符开头的变量 比如$ 或者_开头
function isReserved(str) {
  var c = (str + '').charCodeAt(0);
  return c === 0x24 || c === 0x5f;
}
// 0.1.2 作一层_data的代理
function proxy(target, sourceKey, key) {
  // TODO: 这里就是第一次见开始使用Object.defineProperty 来重新定义vm里面data数据
  // 意味着我们每次访问this.xxx属性 都会去访问this._data.xxx属性
  // 因为在0.1里面我们将data函数里面的值赋值给了_data
  // 好 这里意思搞清楚后 我们回到0.1
  sharedPropertyDefinition.get = function proxyGetter() {
    return this[sourceKey][key];
  };
  sharedPropertyDefinition.set = function proxySetter(val) {
    this[sourceKey][key] = val;
  };
  Object.defineProperty(target, key, sharedPropertyDefinition);
}
// 0.1.3 定义分享属性对象
var sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop,
};
// 0.1.4 空函数 很多地方使用了
function noop(a, b, c) {}
// 0.1.5 获取data
function getData(data, vm) {
  // #7573 disable dep collection when invoking data getters
  // TODO:这里跟之前分析computed在Watcher的原型方法get中调用了pushTarget
  // 唯一的区别就是在那里传了this(Watcher实例)而这里没传值过去
  pushTarget();
  try {
    // TODO:这里以vm实例调用data 即我们在data函数里 this指向vm实例
    // 这里就是作了执行data() 返回我们声明的data值对象{}
    return data.call(vm, vm);
  } catch (e) {
    handleError(e, vm, 'data()');
    return {};
  } finally {
    popTarget();
  }
}
源码4738执行observe(data,true) 此时这个data为根结点数据 添加为响应 见下面
function observe(value, asRootData) {
    // TODO: 这里给值创建一个监听 若已到了最后一层 则返回
    // 比如 return {
    //    name:'test',
    //    list:{
    //      xxx:"data"
    //    }
    // } 初始化进来是第一层 则继续执行
    // 当到了name的时候 已经为最后一层 则return
    // 当到了list还未停止 继续 直到xxx
    if (!isObject(value) || value instanceof VNode) {
        return;
    }
    var ob;
    // TODO: __ob__ 这个属性是在Observer里面才创建的 创建过的就不需要创建了
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__;
    } else if (shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) {
        // TODO: 为值创建响应式  这个很关键
        ob = new Observer(value);
    }
    if (asRootData && ob) {
        ob.vmCount++;
    }
    return ob;
}
var Observer = function Observer(value) {
  this.value = value;
  // TODO:new 一个收集者
  this.dep = new Dep();
  this.vmCount = 0;
  // TODO: 定义value里面的__ob__绑定一个监测类实例 但是不能枚举出来 隐式属性
  def(value, '__ob__', this);
  // TODO: 判断当前value是否为数组
  if (Array.isArray(value)) {
    if (hasProto) {
      protoAugment(value, arrayMethods);
    } else {
      copyAugment(value, arrayMethods, arrayKeys);
    }
    this.observeArray(value);
  } else {
    // TODO: 我们一般的data函数返回对象 走walk方法
    this.walk(value);
  }
};
//  给value定义一个隐藏属性__ob__ 值为监测类实例
function def(obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true,
  });
}
// Observer原型上walk方法
Observer.prototype.walk = function walk(obj) {
  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
      // 定义每个属性的响应式
    defineReactive$$1(obj, keys[i]);
  }
};
// 对一个对象 定义一系列响应式属性
function defineReactive$$1(obj, key, val, customSetter, shallow) {
  // TODO: 这里new一个收集者
  var dep = new Dep();
  // TODO:这里拿到当前data的自有属性描述符 比如value&configurable&get&set等等
  var property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
    return;
  }
  // cater for pre-defined getter/setters
  // TODO: 去拿data函数中设置的数据值的get set
  var getter = property && property.get;
  var setter = property && property.set;
  // TODO: 这里很有趣了 我们在初始化得时候 正常设置data值时 是没得get set方法的
  // 而且在7.2中初始化调用defineReactive$$1方法 入参就是2个 所以进入这个判断
  if ((!getter || setter) && arguments.length === 2) {
    // TODO:将当前当前data中key值给给val
    val = obj[key];
  }
  // TODO: 初始化 这个childOb为undefined
  // TODO:0118 这个childOb 其实就是当我们的data数据为对象时
  /**
   *    比如:data(){
   *        return {
   *          message:'test',
   *          list:{
   *            xxx:'msg'
   *          }
   * 
   *        }    
   *    }
   *    这种在给getter的时候会触发childOb的依赖收集
   */
  var childOb = !shallow && observe(val);
  // TODO:响应式核心来了 为每个data的数据进行defineProperty设置 get&set
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
        // 在访问该属性时 这里有闭包的特性 此时的val还是之前的值 包括dep也是第一次初始化的对象
      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 */
      // TODO: 这里判断一手值是否变化
      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();
    },
  });
}
// 4. 全局函数pushTarget&popTarget&全局变量targetStack
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
Dep.target = null;
var targetStack = [];
function pushTarget(target) {
  // TODO: 在前面的3中调用了pushTarget 这里将从3中的this(Watcher实例)压进targetStack
  targetStack.push(target);
  // TODO: 给Dep的属性target挂载Watcher实例 接下来分析下Dep
  Dep.target = target;
}
function popTarget() {
  targetStack.pop();
  Dep.target = targetStack[targetStack.length - 1];
}
依赖收集中间站(发布者)
var uid = 0;
/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 */
// TODO: 声明全局Dep构造函数也就是全局对象 (发布者)
var Dep = function Dep() {
  this.id = uid++;
  // TODO: 构造函数上挂载subs属性为数组集合 里面存的是每个订阅者也就是watcher
  this.subs = [];
};
// TODO:压入subs方法
Dep.prototype.addSub = function addSub(sub) {
  this.subs.push(sub);
};
// TODO:清除sub方法
Dep.prototype.removeSub = function removeSub(sub) {
  // TODO: 这里调用了个全局工具方法 清除数组指定项
  /**
   *   function remove (arr, item) {
          if (arr.length) {
            var index = arr.indexOf(item);
            if (index > -1) {
              return arr.splice(index, 1)
            }
          }
        }
   * 
   */
  remove(this.subs, sub);
};
// TODO:建立“依赖”方法
Dep.prototype.depend = function depend() {
  // TODO: 这里在初始化的时候 Dep.target值为null
  // 但是我们在前面的3中调用了pushTarget方法 而pushTarget方法中将Dep.target
  // 设置为了那个时间段的Watcher实例
  if (Dep.target) {
    // TODO: 这里又切调用了Watcher实例的addDep方法 接下来分析下Watcher上面的addDep方法
    // 并传入了Dep这个当前实例(上面挂载了id和subs集合)
    Dep.target.addDep(this);
  }
};
// TODO: 建立“通知”方法
Dep.prototype.notify = function notify() {
  // stabilize the subscriber list first
  var subs = this.subs.slice();
  if (!config.async) {
    // subs aren't sorted in scheduler if not running async
    // we need to sort them now to make sure they fire in correct
    // order
    subs.sort(function (a, b) {
      return a.id - b.id;
    });
  }
  for (var i = 0, l = subs.length; i < l; i++) {
      // 关键的步骤 执行订阅者的update 
    subs[i].update();
  }
};
(观察者&订阅者)
var uid$2 = 0;
var Watcher = function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
  this.vm = vm;
  if (isRenderWatcher) {
    vm._watcher = this;
  }
  // TODO: 将当前this指向的Watcher实例压进vue实例下私有属性_watchers
  vm._watchers.push(this);
  if (options) {
    this.deep = !!options.deep;
    this.user = !!options.user;
    this.lazy = !!options.lazy;
    this.sync = !!options.sync;
    this.before = options.before;
  } else {
    this.deep = this.user = this.lazy = this.sync = false;
  }
  this.cb = cb;
  this.id = ++uid$2; // uid for batching
  this.active = true;
  this.dirty = this.lazy; // for lazy watchers
  this.deps = [];
  this.newDeps = [];
  // TODO: 这里判断是否支持Set不支持就模拟Set(并提供has&add&clear内部方法)
  this.depIds = new _Set();
  this.newDepIds = new _Set();
  // TODO: 这里将方法字符串化
  this.expression = expOrFn.toString();
  if (typeof expOrFn === 'function') {
    // TODO: 这里将执行方法挂载到Watcher实例的getter属性上
    this.getter = expOrFn;
  } else {
    this.getter = parsePath(expOrFn);
    if (!this.getter) {
      this.getter = noop;
      warn('Failed watching path: "' + expOrFn + '" ' + 'Watcher only accepts simple dot-delimited paths. ' + 'For full control, use a function instead.', vm);
    }
  }
  // TODO: 初始化是当前value值均为undefined 后面调用Watcher实例的get()方法
  this.value = this.lazy ? undefined : this.get();
};
// 3. Watcher原型上的get方法
Watcher.prototype.get = function get() {
  // TODO: 调用了全局函数pushTarget 这里跟另一个模块Dep有关了
  // 顺势找到Dep分析下这个响应式
  pushTarget(this);
  // TODO:执行这步的意义何在
  // 就是为了给全局对象Dep的target属性挂载上当前的Watcher实例
  var value;
  var vm = this.vm;
  try {
    // TODO: 执行前端编写的函数 内部this指向vue实例 传参vue实例
    // TODO: 0118 这个地方的getter还不是这么简单的理解
    // 多数情况下他是一个updateComponent函数
    value = this.getter.call(vm, vm);
  } catch (e) {
    if (this.user) {
      handleError(e, vm, 'getter for watcher "' + this.expression + '"');
    } else {
      throw e;
    }
  } finally {
    if (this.deep) {
      traverse(value);
    }
    // TODO: 每次执行完this.getter后 释放全局变量targetStack 且置
    // Dep.target为null 这里就是每次作update时保持一个watcher执行
    popTarget();
    this.cleanupDeps();
  }
  return value;
};
// 3.1 清理依赖
Watcher.prototype.cleanupDeps = function cleanupDeps () {
  var i = this.deps.length;
  while (i--) {
    var dep = this.deps[i];
    if (!this.newDepIds.has(dep.id)) {
      dep.removeSub(this);
    }
  }
  var tmp = this.depIds;
  this.depIds = this.newDepIds;
  this.newDepIds = tmp;
  this.newDepIds.clear();
  tmp = this.deps;
  this.deps = this.newDeps;
  this.newDeps = tmp;
  this.newDeps.length = 0;
};
Watcher.prototype.addDep = function addDep(dep) {
  var id = dep.id;
  // TODO: 判断当前id是否已在Watcher实例的newDepIds属性里面
  // 这里的newDepIds是一个Set数据结构属性
  if (!this.newDepIds.has(id)) {
    // TODO: 没得就add进去
    this.newDepIds.add(id);
    // TODO: 这里newDeps是数组结构 将当前dep push进去
    this.newDeps.push(dep);
    if (!this.depIds.has(id)) {
      // TODO:同理这里调用的是当前dep实例上的addSub 也就是将当前的Watcher实例
      // 压进dep的subs数组里面
      dep.addSub(this);
    }
  }
  // TODO:这步完了后 又回到了3里面的pushTarget后续逻辑
};
Watcher.prototype.update = function update() {
  /* istanbul ignore else */
  if (this.lazy) {
    this.dirty = true;
  } else if (this.sync) {
    this.run();
  } else {
    queueWatcher(this);
  }
};
Watcher.prototype.run = function run() {
  if (this.active) {
    var value = this.get();
    if (
      value !== this.value ||
      // Deep watchers and watchers on Object/Arrays should fire even
      // when the value is the same, because the value may
      // have mutated.
      isObject(value) ||
      this.deep
    ) {
      // set new value
      var oldValue = this.value;
      this.value = value;
      if (this.user) {
        try {
          this.cb.call(this.vm, value, oldValue);
        } catch (e) {
          handleError(e, this.vm, 'callback for watcher "' + this.expression + '"');
        }
      } else {
        this.cb.call(this.vm, value, oldValue);
      }
    }
  }
};
watcher队列处理
/**
 * Push a watcher into the watcher queue.
 * Jobs with duplicate IDs will be skipped unless it's
 * pushed when the queue is being flushed.
 */
function queueWatcher(watcher) {
  var id = watcher.id;
  if (has[id] == null) {
    has[id] = true;
    if (!flushing) {
      queue.push(watcher);
    } else {
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      var i = queue.length - 1;
      while (i > index && queue[i].id > watcher.id) {
        i--;
      }
      queue.splice(i + 1, 0, watcher);
    }
    // queue the flush
    if (!waiting) {
      waiting = true;

      if (!config.async) {
        flushSchedulerQueue();
        return;
      }
      nextTick(flushSchedulerQueue);
    }
  }
}
/**
 * Flush both queues and run the watchers.
 */
function flushSchedulerQueue() {
  currentFlushTimestamp = getNow();
  flushing = true;
  var watcher, id;

  // Sort queue before flush.
  // This ensures that:
  // 1. Components are updated from parent to child. (because parent is always
  //    created before the child)
  // 2. A component's user watchers are run before its render watcher (because
  //    user watchers are created before the render watcher)
  // 3. If a component is destroyed during a parent component's watcher run,
  //    its watchers can be skipped.
  queue.sort(function (a, b) {
    return a.id - b.id;
  });

  // do not cache length because more watchers might be pushed
  // as we run existing watchers
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index];
    if (watcher.before) {
      watcher.before();
    }
    id = watcher.id;
    has[id] = null;
    watcher.run();
    // in dev build, check and stop circular updates.
    if (has[id] != null) {
      circular[id] = (circular[id] || 0) + 1;
      if (circular[id] > MAX_UPDATE_COUNT) {
        warn('You may have an infinite update loop ' + (watcher.user ? 'in watcher with expression "' + watcher.expression + '"' : 'in a component render function.'), watcher.vm);
        break;
      }
    }
  }

  // keep copies of post queues before resetting state
  var activatedQueue = activatedChildren.slice();
  var updatedQueue = queue.slice();

  resetSchedulerState();

  // call component updated and activated hooks
  callActivatedHooks(activatedQueue);
  callUpdatedHooks(updatedQueue);

  // devtool hook
  /* istanbul ignore if */
  if (devtools && config.devtools) {
    devtools.emit('flush');
  }
}

以上就是vue响应式核心部分(设计模式:发布-订阅模式

  1. data(作一层proxy代理访问_data)

  2. observe(为data主动构建监测数据)

  3. new Observer() (主动绑定__ob__属性 调用walk方法 )

  4. 遍历每个Object对象 (为对象属性进行defineReactive$$1方法)

  5. Object.defineProperty (每个属性生成一次收集者且会判断是否多层对象嵌套再次调用b步骤直到每个属性均可响应式;定义属性get(利用函数闭包特性 收集当前依赖项)定义属性set(判断值是否完全变化,且给新值再次添加b步骤响应式属性 最后发布依赖))

  6. 手动调用挂载方法vm.$mount(’#app’) (执行mountComponent方法->触发beforeMount钩子函数->生成updateComponent方法->vm._update(vm._render(),hydrating)

  7. 创建观察者Watcher(加入brefore->钩子回调触发beforeUpdate钩子函数->绑定getter方法为updateComponent方法)

  8. 触发Watcher get方法(pushTarget置全局依赖标识Dep.target为当前watcher实例)

  9. 调用步骤g中的getter方法(就是调用vm._update(vm._render(),hydrating)

    // 次部分代码涉及关于VNode等渲染问题 是另一个值得研究的问题

  10. 调用vm._render() (其实就是执行以with包括的方法体)

  11. 调用vm._update()

  12. 触发拿值属性步骤e中的get (收集依赖)

  13. 虚拟dom到真实dom渲染完毕

    //

  14. 调用popTarget (置当前全局依赖标识Dep.target为undefined)

  15. 调用当前观察者watcher清除依赖方法(将newDepIds、newDeps赋值给depIds、deps后再置为空以便接收下一次更新)

  16. 触发生命周期钩子“mounted”

    1.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值