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 相关文章推荐
jQuery函数的第二个参数获取指定上下文中的DOM元素
May 19 Javascript
jquery实现人性化的有选择性禁用鼠标右键
Jun 30 Javascript
js结合正则实现国内手机号段校验
Jun 19 Javascript
bootstrap-Treeview实现级联勾选
Nov 23 Javascript
js实时监控文本框输入字数的实例代码
Jan 18 Javascript
原生JS实现多个小球碰撞反弹效果示例
Jan 31 Javascript
vue底部加载更多的实例代码
Jun 29 Javascript
微信小程序倒计时功能实例代码
Jul 17 Javascript
解决vue 单文件组件中样式加载问题
Apr 24 Javascript
JS代码屏蔽F12,右键,粘贴,复制,剪切,选中,操作实例
Sep 17 Javascript
ant design 日期格式化的实现
Oct 27 Javascript
Vue + ts实现轮播插件的示例
Nov 10 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
在WIN98下以apache模块方式安装php
2006/10/09 PHP
详解php中反射的应用
2016/03/15 PHP
php正则表达式使用方法整理集合
2020/01/31 PHP
理解JavaScript的caller,callee,call,apply
2009/04/28 Javascript
javascript删除option选项的多种方法总结
2013/11/22 Javascript
如何实现textarea里的不同文本显示不同颜色
2014/01/20 Javascript
使用JavaScript开发IE浏览器本地插件实例
2015/02/18 Javascript
JavaScript获得url所有参数键值表的方法
2015/03/21 Javascript
jquery实现的伪分页效果代码
2015/10/29 Javascript
javascript下拉列表菜单的实现方法
2015/11/18 Javascript
AngularJS创建自定义指令的方法详解
2016/11/03 Javascript
nodejs处理图片的中间件node-images详解
2017/05/08 NodeJs
Vue.2.0.5实现Class 与 Style 绑定的实例
2017/06/20 Javascript
webpack处理 css\less\sass 样式的方法
2017/08/21 Javascript
react-native-tab-navigator组件的基本使用示例代码
2017/09/07 Javascript
vue下使用nginx刷新页面404的问题解决
2019/08/02 Javascript
中级前端工程师必须要掌握的27个JavaScript 技巧(干货总结)
2019/09/23 Javascript
5个你不知道的JavaScript字符串处理库(小结)
2020/06/01 Javascript
Vue父子组件传值的一些坑
2020/09/16 Javascript
Python编写一个闹钟功能
2017/07/11 Python
windows下Virtualenvwrapper安装教程
2017/12/13 Python
python实现Adapter模式实例代码
2018/02/09 Python
Django 使用logging打印日志的实例
2018/04/28 Python
Python判断中文字符串是否相等的实例
2018/07/06 Python
python实现五子棋小游戏
2020/03/25 Python
python查看文件大小和文件夹内容的方法
2019/07/08 Python
PyCharm如何导入python项目的方法
2020/02/06 Python
Free People中国官网:波西米亚风格女装服饰
2016/08/30 全球购物
英国折扣高尔夫商店:Discount Golf Store
2019/11/19 全球购物
西班牙在线光学:Visual-Click
2020/06/22 全球购物
大学生就业自我鉴定
2013/10/26 职场文书
民政局个人整改措施
2014/09/24 职场文书
保密工作整改情况汇报
2014/11/06 职场文书
用Python制作灯光秀短视频的思路详解
2021/04/13 Python
pytorch实现手写数字图片识别
2021/05/20 Python
Win11 21h2可以升级22h2吗?看看你的电脑符不符合要求
2022/07/07 数码科技