Vue之Watcher源码解析(1)


Posted in Javascript onJuly 19, 2017

上一节最后再次调用了mount函数,我发现竟然跳到了7000多行的那个函数,之前我还说因为声明早了被覆盖,看来我错了!

就是这个函数:

// Line-7531
  Vue$3.prototype.$mount = function(el, hydrating) {
    el = el && inBrowser ? query(el) : undefined;
    return mountComponent(this, el, hydrating)
  };

第一步query就不用看了,el此时是一个DOM节点,所以直接返回,然后调用了mountComponent函数。

// Line-2375
  function mountComponent(vm, el, hydrating) {
    vm.$el = el;
    /* 检测vm.$options.render */

    // 调用钩子函数
    callHook(vm, 'beforeMount');

    var updateComponent;
    /* istanbul ignore if */
    if ("development" !== 'production' && config.performance && mark) {
      /* 标记vue-perf */
    } else {
      updateComponent = function() {
        vm._update(vm._render(), hydrating);
      };
    }

    // 生成中间件watcher
    vm._watcher = new Watcher(vm, updateComponent, noop);
    hydrating = false;

    // 调用最后一个钩子函数
    if (vm.$vnode == null) {
      vm._isMounted = true;
      callHook(vm, 'mounted');
    }
    return vm
  }

这个函数做了三件事,调用beforeMount钩子函数,生成Watcher对象,接着调用mounted钩子函数。

数据双绑、AST对象处理完后,这里的Watcher对象负责将两者联系到一起,上一张网上的图片:

Vue之Watcher源码解析(1)

可以看到,之前以前把所有的组件都过了一遍,目前就剩一个Watcher了。

构造新的Watcher对象传了3个参数,当前vue实例、updateComponent函数、空函数。

// Line-2697
  var Watcher = function Watcher(vm, expOrFn, cb, options) {
    this.vm = vm;
    // 当前Watcher添加到vue实例上
    vm._watchers.push(this);
    // 参数配置 默认为false
    if (options) {
      this.deep = !!options.deep;
      this.user = !!options.user;
      this.lazy = !!options.lazy;
      this.sync = !!options.sync;
    } else {
      this.deep = this.user = this.lazy = this.sync = false;
    }
    this.cb = cb;
    this.id = ++uid$2;
    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 = function() {};
        "development" !== 'production' && warn(
          "Failed watching path: \"" + expOrFn + "\" " +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        );
      }
    }
    // 不是懒加载类型调用get
    this.value = this.lazy ?
      undefined :
      this.get();
  };

该构造函数添加了一堆属性,第二个参数由于是函数,直接作为getter属性加到watcher上,将字符串后则作为expression属性。

最后有一个value属性,由于lazy为false,调用原型函数gei进行赋值:

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

  // Line-750
  Dep.target = null;
  var targetStack = [];

  function pushTarget(_target) {
    // 默认为null 
    if (Dep.target) {
      targetStack.push(Dep.target);
    }
    // 依赖目前标记为当前watcher
    Dep.target = _target;
  }

  function popTarget() {
    Dep.target = targetStack.pop();
  }

原型方法get中,先设置了依赖收集数组Dep的target值,user属性暂时不清楚意思,跳到了else分支,调用了getter函数。而getter就是之前的updateComponent函数:

// Line-2422
  updateComponent = function() {
    vm._update(vm._render(), hydrating);
  };

这个函数不接受参数,所以说传进来的两个vm并没有什么卵用,调用这个函数会接着调用_update函数,这个是挂载到vue原型的方法:

// Line-2422
  Vue.prototype._render = function() {
    var vm = this;
    var ref = vm.$options;
    var render = ref.render;
    var staticRenderFns = ref.staticRenderFns;
    var _parentVnode = ref._parentVnode;
    // 检测是否已挂载
    if (vm._isMounted) {
      // clone slot nodes on re-renders
      for (var key in vm.$slots) {
        vm.$slots[key] = cloneVNodes(vm.$slots[key]);
      }
    }
    // 都没有
    vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject;
    if (staticRenderFns && !vm._staticTrees) {
      vm._staticTrees = [];
    }
    vm.$vnode = _parentVnode;
    // render self
    var vnode;
    try {
      // 调用之前的render字符串函数
      vnode = render.call(vm._renderProxy, vm.$createElement);
    } catch (e) {
      /* handler error */
    }
    // return empty vnode in case the render function errored out
    if (!(vnode instanceof VNode)) {
      /* 报错 */
      vnode = createEmptyVNode();
    }
    // set parent
    vnode.parent = _parentVnode;
    return vnode
  };

方法获取了一些vue实例的参数,比较重点的是render函数,调用了之前字符串后的ast对象:

Vue之Watcher源码解析(1)

在这里有点不一样的地方,接下来的跳转有点蒙,下节再说。

Vue之Watcher源码解析(1)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript中String类的subString()方法和slice()方法
May 24 Javascript
jQuery调用AJAX时Get和post公用的乱码解决方法实例说明
Jun 04 Javascript
window.onresize 多次触发的解决方法
Nov 08 Javascript
js创建元素(节点)示例
Jan 02 Javascript
javascript生成随机颜色示例代码
May 05 Javascript
输入框过滤非数字的js代码
Sep 18 Javascript
jquery用ajax方式从后台获取json数据后如何将内容填充到下拉列表
Aug 26 Javascript
解决angular的post请求后SpringMVC后台接收不到参数值问题的方法
Dec 10 Javascript
Vue.js在使用中的一些注意知识点
Apr 29 Javascript
ReactNative Image组件使用详解
Aug 07 Javascript
6种JavaScript继承方式及优缺点(小结)
Feb 06 Javascript
vue中是怎样监听数组变化的
Oct 24 Javascript
angular.js + require.js构建模块化单页面应用的方法步骤
Jul 19 #Javascript
Vue学习笔记进阶篇之多元素及多组件过渡
Jul 19 #Javascript
vue中的非父子间的通讯问题简单的实例代码
Jul 19 #Javascript
Vue之Watcher源码解析(2)
Jul 19 #Javascript
Angular.js项目中使用gulp实现自动化构建以及压缩打包详解
Jul 19 #Javascript
JS+canvas实现的五子棋游戏【人机大战版】
Jul 19 #Javascript
Vue学习笔记进阶篇之vue-router安装及使用方法
Jul 19 #Javascript
You might like
PHP完整的日历类(CLASS)
2006/11/27 PHP
用PHP控制用户的浏览器--ob*函数的使用说明
2007/03/16 PHP
深入理解用mysql_fetch_row()以数组的形式返回查询结果
2013/06/05 PHP
解析PHPExcel使用的常用说明以及把PHPExcel整合进CI框架的介绍
2013/06/24 PHP
php创建类并调用的实例方法
2019/09/25 PHP
Gird事件机制初级读本
2007/03/10 Javascript
jQuery使用手册之二 DOM操作
2007/03/24 Javascript
Js 获取HTML DOM节点元素的方法小结
2009/04/24 Javascript
使用jquery解析XML的方法
2014/09/05 Javascript
jQuery实现Flash效果上下翻动的中英文导航菜单代码
2015/09/22 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
2016/11/06 Javascript
详解为Angular.js内置$http服务添加拦截器的方法
2016/12/20 Javascript
浅谈如何使用 webpack 优化资源
2017/10/20 Javascript
js设置随机切换背景图片的简单实例
2017/11/12 Javascript
vue中使用element-ui进行表单验证的实例代码
2018/06/22 Javascript
Angular5中提取公共组件之radio list的实例代码
2018/07/10 Javascript
在Bootstrap开发框架中使用dataTable直接录入表格行数据的方法
2018/10/25 Javascript
详解关于JSON.parse()和JSON.stringify()的性能小测试
2019/03/14 Javascript
栈和队列数据结构的基本概念及其相关的Python实现
2015/08/24 Python
Python复制文件操作实例详解
2015/11/10 Python
Python按行读取文件的简单实现方法
2016/06/22 Python
python opencv设置摄像头分辨率以及各个参数的方法
2018/04/02 Python
浅谈python配置与使用OpenCV踩的一些坑
2018/04/02 Python
pytorch如何冻结某层参数的实现
2020/01/10 Python
Python数据正态性检验实现过程
2020/04/18 Python
matplotlib 生成的图像中无法显示中文字符的解决方法
2020/06/10 Python
Python OpenCV去除字母后面的杂线操作
2020/07/05 Python
python性能测试工具locust的使用
2020/12/28 Python
NIHAOMARKET官方海外旗舰店:意大利你好华人超市
2018/01/27 全球购物
什么叫应用程序域?什么是托管代码?什么是强类型系统?什么是装箱和拆箱?什么是重载?CTS、CLS和CLR分别作何解释?
2012/05/23 面试题
简述安装Slackware Linux系统的过程
2012/05/08 面试题
关于青春的演讲稿800字
2014/08/22 职场文书
销售人员管理制度
2015/08/06 职场文书
四年级作文之植物
2019/09/20 职场文书
python如何读取.mtx文件
2021/04/22 Python
openstack云计算keystone组件工作介绍
2022/04/20 Servers