接上,watcher构造函数:

var Watcher = function Watcher (
    vm,
    expOrFn,
    cb,
    options,
    isRenderWatcher
  ) {
    this.vm = vm;
    if (isRenderWatcher) {
      vm._watcher = this;
    }
    vm._watchers.push(this);
    // options
    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 = [];
    this.depIds = new _Set();
    this.newDepIds = new _Set();
    this.expression = expOrFn.toString();
    // parse expression for getter
    if (typeof expOrFn === 'function') {
      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
        );
      }
    }
    this.value = this.lazy
      ? undefined
      : this.get();
  };

watcher构造函数,首先和当前vm组件相互引用,然后初始化了一些下面执行中会用到的属性,然后检查expOrFn是否符合要求,有则用之,无则警告,最后执行:

this.value = this.lazy
      ? undefined
      : this.get();

因为this.lazy为false,进入Wathcer.prototype.get方法:

Watcher.prototype.get = function get () {
    pushTarget(this);
    var value;
    var vm = this.vm;
    try {
      value = this.getter.call(vm, vm);
    } catch (e) {
      if (this.user) {
        handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value);
      }
      popTarget();
      this.cleanupDeps();
    }
    return value
  };

这个时候我们发现他调用了pushTarget方法:

function pushTarget (target) {
    targetStack.push(target);
    Dep.target = target;
  }

pushTarget方法很简单,就是将当前watcher实例push进了一个叫做targetStack的栈中,然后将Dep.target设置为当前watcher实例的引用,仔细想想你在哪里看到过这个,Dep.target

想起来没,就是设置data响应式数据的时候,get里的那个Dep.target,所以有了Dep.target,意味着我们要开始依赖收集了,然后代码继续走回到 Watcher.prototype.get中,调用了this.getter.call(vm, vm),这个方法其实就是我们上边说的updateComponent方法。然后走:

 vm._update(vm._render(), hydrating);

这里先会执行vm._render(),hydrating是一个变量表示是否是服务端生成模式,大可不必关心,我们现在要执行的是vm._render:

Vue.prototype._render = function () {
      var vm = this;
      var ref = vm.$options;
      var render = ref.render;
      var _parentVnode = ref._parentVnode;       if (_parentVnode) {
        vm.$scopedSlots = normalizeScopedSlots(
          _parentVnode.data.scopedSlots,
          vm.$slots,
          vm.$scopedSlots
        );
      }       // set parent vnode. this allows render functions to have access
      // to the data on the placeholder node.
      vm.$vnode = _parentVnode;
      // render self
      var vnode;
      try {
        // There's no need to maintain a stack becaues all render fns are called
        // separately from one another. Nested component's render fns are called
        // when parent component is patched.
        currentRenderingInstance = vm;
        vnode = render.call(vm._renderProxy, vm.$createElement);
      } catch (e) {
        handleError(e, vm, "render");
        // return error render result,
        // or previous vnode to prevent render error causing blank component
        /* istanbul ignore else */
        if (vm.$options.renderError) {
          try {
            vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);
          } catch (e) {
            handleError(e, vm, "renderError");
            vnode = vm._vnode;
          }
        } else {
          vnode = vm._vnode;
        }
      } finally {
        currentRenderingInstance = null;
      }
      // if the returned array contains only a single node, allow it
      if (Array.isArray(vnode) && vnode.length === 1) {
        vnode = vnode[0];
      }
      // return empty vnode in case the render function errored out
      if (!(vnode instanceof VNode)) {
        if (Array.isArray(vnode)) {
          warn(
            'Multiple root nodes returned from render function. Render function ' +
            'should return a single root node.',
            vm
          );
        }
        vnode = createEmptyVNode();
      }
      // set parent
      vnode.parent = _parentVnode;
      return vnode
    };
  }

这里他主要做了几件事呢,其实就是将render函数,当前实例,当前实例的相关配置包含data那些东西取出来,因为我们_parentVnode为空,故直接走try里的render,render执行的时候是上下文为我们最开始定义的那个代理的对象,传进去了一个叫做vm.$createElement的方法,这个方法是在initRender的时候创建的,用于创建一个VNode的节点,然后进入render函数,因为render函数这个玩意啊,像我们一般人写的时候基本上不咋写,这里我们看看他长啥样子:

function anonymous() {
    with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(msg))])}
}

好啦,解释一下with干了个啥,相当于将this也就是vm._renderProxy加入到了当前作用域链的头,那么这个时候with里边的语句的变量可以访问到的东西第一个能访问的就是vm._renderProxy里的,然后执行_s(msg)的时候因为是一个读值操作,故而,触发了get方法,由于之前将vm.key,代理到了vm._data.key,所以首先会触发,vm.key的get方法:

sharedPropertyDefinition.get = function proxyGetter () {
      return this[sourceKey][key]
    };

然后,在执行this[sourceKey][key]的时候会触发data上响应式数据的get方法,也就是这个时候开始收集依赖,并且见到了我们的Dep.target:

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
      }

前面说过Dep.target就是当前vue实例的watcher,然后执行dep.depend:

function depend () {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  };

这里又调用了watcher的addDep并把当前的dep实例传了过去:

 /**
   * Add a dependency to this directive.
   */
  Watcher.prototype.addDep = function addDep (dep) {
    var id = dep.id;
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id);
      this.newDeps.push(dep);
      if (!this.depIds.has(id)) {
        dep.addSub(this);
      }
    }
  };

判断了几下有没有之后,最终将watcher实例加入到了dep实例的subs数组中,get执行完毕,回到render函数中。

楼下继续

写于vue3.0发布前夕的helloworld之三的更多相关文章

  1. VUE3.0发布,自己搞个文档网站

    9月19日,尤大神发表了VUE3.0版本的演说,强大且震撼,这两天一直在找网站文档,可能还未被百度收录,未找到文档网站.后来在github上面找到了中文代码. 地址为:https://github.c ...

  2. 预计2019年发布的Vue3.0到底有什么不一样的地方?

    摘要: Vue 3.0预览. 原文:预计今年发布的Vue3.0到底有什么不一样的地方? 作者:小肆 微信公众号:技术放肆聊 Fundebug经授权转载,版权归原作者所有. 还有几个月距离 vue2 的 ...

  3. 把酒言欢话聊天,基于Vue3.0+Tornado6.1+Redis发布订阅(pubsub)模式打造异步非阻塞(aioredis)实时(websocket)通信聊天系统

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_202 "表达欲"是人类成长史上的强大"源动力",恩格斯早就直截了当地指出,处在蒙昧时代即低 ...

  4. 纯小白入手 vue3.0 CLI - 3.3 - 路由的导航守卫

    vue3.0 CLI 真小白一步一步入手全教程系列:https://www.cnblogs.com/ndos/category/1295752.html 尽量把纷繁的知识,肢解重组成为可以堆砌的知识. ...

  5. 纯小白入手 vue3.0 CLI - 2.1 - 组件 ( component )

    vue3.0 CLI 真小白入手全教程系列:https://www.cnblogs.com/ndos/category/1295752.html 我的 github 地址 - vue3.0Study  ...

  6. 快速进阶Vue3.0

    在2019.10.5日发布了Vue3.0预览版源码,但是预计最早需要等到 2020 年第一季度才有可能发布 3.0 正式版. 可以直接看 github源码. 新版Vue 3.0计划并已实现的主要架构改 ...

  7. Vue3实战系列:Vue3.0 + Vant3.0 搭建种子项目

    最近在用 Vue3 写一个开源的商城项目,开源后让大家也可以用现成的 Vue3 大型商城项目源码来练练手,目前处于开发阶段,过程中用到了 Vant3.0,于是就整理了这篇文章来讲一下如何使用 Vue3 ...

  8. Rubinius 2.0 发布,Ruby 虚拟机

    Rubinius 2.0 发布了,官方发行说明请看这里. Rubinius是一个运行Ruby程序的虚拟机,其带有Ruby的核心库. Rubinius的设计决定了其调试功能的强大,使得在运行时常规的Ru ...

  9. Restful.Data v2.0发布,谢谢你们的支持和鼓励

    v1.0发布后,承蒙各位博友们的热心关注,也给我不少意见和建议,在此我真诚的感谢 @冰麟轻武 等朋友,你们的支持和鼓励,是这个开源项目最大的推动力. v2.0在除了细枝末节外,在功能上主要做了一下更新 ...

  10. 开源搜索引擎Iveely 0.7.0发布,不一样,那就让他不一样!

    2012年08月05日,Iveely Search Engine 0.1.0发布,今天,怀着对于未来的追求,终于,0.7.0如期和大家见面了,7个版本,历时2年4个月,感谢大家的支持,感谢我不离不弃的 ...

随机推荐

  1. Vue2安装less版本过高问题,需要降级

    安装指定less版本解决: -D: 本地安装 -g: 全局安装 npm install less@3.9.0 less-loader@5.0.0 -D

  2. CPU L1,L2,L3多级缓存的基本作用

    基本作用 加快CPU与主内存之间的数据交换. 区别 缓存类型 L1 L2 L3 位置 最靠近CPU核心 次之 再次之 容量 一般几十KB~几百KB 几百KB~几MB 几MB~几十MB 速度 几个时钟周 ...

  3. hdu-2544 最短路(SPFA)

    SPFA整体过程 1.用一个队列queue支撑. 2.dis[i]表示目前x到i的距离. 3.b[i]表示i是否在q中. 4.清空队列while(q.size()) q.pop();. 5.初始化(把 ...

  4. Date时间API

    JDK8之前时间API   1. java.lang.System类     System类提供的public static long currentTimeMillis()用来返回当前时间与1970 ...

  5. 使用iperf测试网卡性能

    1.目标 测试网卡通信性能,同时可以通过改变连接方式(从两台PC网线直连,切换到通过交换机连接)测试交换机最高速率性能. 2.使用工具 硬件:两台PC机(本例用win10 64位).数根网线.交换机 ...

  6. Unity 使用IO流获取PNG/JPG/GIF/BMP的宽高【转】

    原文链接:https://blog.csdn.net/flj135792468/article/details/107963280?utm_medium=distribute.pc_relevant. ...

  7. vue指令入门

    1.  vue属性.事件.内容绑定 1 <div id="dv"> 2 <!-- v-cloak能够解决表达式闪烁问题 3 (当网速较慢时,会先出现{{msg}} ...

  8. Linux系统开机自启动jar包程序

    一.编写jenkins开机自启动脚本 vim /etc/rc.d/init.d/jenkins.sh #!/bin/bash export JAVA_HOME=/usr/lib/jvm/java ex ...

  9. git连接不上,使用git镜像

    github有一个镜像网站:把github替换为kgithub

  10. Kubernetes--Pod对象的生命周期

    Pod对象自从其创建开始至其终止退出的时间范围称为其生命周期.在这段时间中,Pod会处于多种不同的状态,并执行一些操作:其中,创建主容器(main container)为必需的操作,其他可选的操作还包 ...