接上,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. 负载均衡做集群时关于的session不均衡的解决方案

    从用户端来解释,就是当一个用户第一次访问被负载均衡代理到后端服务器A并登录后,服务器A上保留了用户的登录信息:当用户再次发送请求时, 根据负载均衡策略可能被代理到后端不同的服务器,例如服务器B,由于这 ...

  2. 【C学习笔记】day4-2 求出0~999之间的所有“水仙花数”并输出。

    2.求出0-999之间的所有"水仙花数"并输出."水仙花数"是指一个三位数,其各位数字的立方和确好等于该数本身,如:153=1+5+3?,则153是一个&quo ...

  3. class和struct区别

    类(class) 引用类型:将一个对象赋值给另一个对象时,系统不会对此对象进行拷贝,而会将指向这个对象的指针赋值给另一个对象,当修改其中一个对象的值时,另一个对象的值会随之改变. 结构体(struct ...

  4. 对APP渗透测试之抓包

    一.APP渗透测试的原理 APP(手机软件),APP测试和web测试基本没有区别,一个网站存在SQL注入,用PC端浏览器去访问存在SQL注入漏洞,用手机浏览器去访问一样也存在SQL注入漏洞. 大部分漏 ...

  5. apk文件查看指纹证书方法

    1. 先将apk文件重命名为zip文件 2. 解压zip到指定一个文件夹下,其中的META-INF/CERT.RSA文件即MD5签名文件 3. cmd下打开窗口,输入如下命令: keytool -pr ...

  6. 安全测试-WEB安全渗透测试基础知识(一)

     1.1. Web技术演化  1.1.1. 静态页面 在互联网最初开始的时候,Web网站的主要内容是静态的,由文字和图片组成,制作和表现形式也是以表格为主.当时的用户行为也非常简单,仅仅是浏览网页. ...

  7. 3、k8s 核心实战

    7 kubernets核心实战 7.1 资源创建方式 命令行 yaml 7.2 namespace 名称空间来隔离资源 命令行方式 kubectl create ns hello kubectl de ...

  8. 「postOI」以另一种方式证明 FWT

    记号 \(\otimes\) 代表或/与/异或卷积: \(\oplus\) 代表"拼接",例如 \(A\oplus B\) 即将 \(B\) 接在 \(A\) 的后面: \(+,- ...

  9. 小僧の神様・城の崎にて.PDF

    书本详情 小僧の神様·城の崎にて作者:志賀直哉出版社:新潮社时间:1968.07页数:270 ISBN:  

  10. Win10删除此电脑默认的7个文件夹

    删除方法 用记事本拷贝以下内容,改文件后缀为reg,然后点击执行. Windows Registry Editor Version 5.00 ;如需还原去除上语句前减号即可 ;取消我的电脑" ...