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 textContent与innerText的异同分析
Oct 22 Javascript
jQuery之网页换肤实现代码
Apr 30 Javascript
JavaScript常用小技巧小结
Dec 29 Javascript
高效Web开发的10个jQuery代码片段
Jul 22 Javascript
js设置文字颜色的方法示例
Dec 30 Javascript
js阻止移动端页面滚动的两种方法
Jan 25 Javascript
vue项目中引入noVNC远程桌面的方法
Mar 05 Javascript
jQuery实现的鼠标拖动画矩形框示例【可兼容IE8】
May 17 jQuery
轻松学习JavaScript函数中的 Rest 参数
May 30 Javascript
js定义类的方法示例【ES5与ES6】
Jul 30 Javascript
vue实践---根据不同环境,自动转换请求的url地址操作
Sep 21 Javascript
swiper4实现移动端导航栏tab滑动切换
Oct 16 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
用文本文件实现的动态实时发布新闻的程序
2006/10/09 PHP
一个自定义位数的php多用户计数器代码
2007/03/11 PHP
PHP常用函数小技巧
2008/09/11 PHP
PHP实现的交通银行网银在线支付接口ECSHOP插件和使用例子
2014/05/10 PHP
开启PHP Static 关键字之旅模式
2015/11/13 PHP
PHP函数func_num_args用法实例分析
2015/12/07 PHP
利用PHP访问带有密码的Redis方法示例
2017/02/09 PHP
简单谈谈PHP面向对象之标识对象
2017/06/27 PHP
JavaScript中各种编码解码函数的区别和注意事项
2010/08/19 Javascript
javascript与webservice的通信实现代码
2010/12/25 Javascript
jquery随机展示头像代码
2011/12/21 Javascript
jquery加载图片时以淡入方式显示的方法
2015/01/14 Javascript
JavaScript简单表格编辑功能实现方法
2015/04/16 Javascript
js实现上传文件添加和删除文件选择框
2016/10/24 Javascript
jquery html5 视频播放控制代码
2016/11/06 Javascript
20行JS代码实现网页刮刮乐效果
2017/06/23 Javascript
Bootstrap Table快速完美搭建后台管理系统
2017/09/20 Javascript
vue中axios请求的封装实例代码
2019/03/23 Javascript
vue-router 起步步骤详解
2019/03/26 Javascript
extjs图表绘制之条形图实现方法分析
2020/03/06 Javascript
python实现根据主机名字获得所有ip地址的方法
2015/06/28 Python
Python3.5 Json与pickle实现数据序列化与反序列化操作示例
2019/04/29 Python
Python中字符串与编码示例代码
2019/05/20 Python
Python Serial串口基本操作(收发数据)
2020/11/06 Python
selenium+python实现基本自动化测试的示例代码
2021/01/27 Python
房地产还款计划书
2014/01/10 职场文书
冰淇淋开店创业计划书
2014/02/01 职场文书
新护士岗前培训制度
2014/02/02 职场文书
党的群众路线对照检查材料
2014/08/27 职场文书
2015年五一劳动节活动总结
2015/02/09 职场文书
高中生综合素质自我评价
2015/03/06 职场文书
2015年教研员工作总结
2015/05/26 职场文书
工作表现证明
2015/06/15 职场文书
2015年秋季校长开学典礼致辞
2015/07/29 职场文书
环保建议书范文
2015/09/14 职场文书
Vue3.0写自定义指令的简单步骤记录
2021/06/27 Vue.js