当前位置: 代码网 > it编程>前端脚本>Vue.js > 关于Vue实例创建的整体流程

关于Vue实例创建的整体流程

2024年06月10日 Vue.js 我要评论
前言本篇是分析new vue()过程相关的流程,主要了解如下两点:new vue()整体处理流程挂载处理流程整体逻辑使用vue构造函数创建vue实例,具体的处理逻辑如下图:从上图中可以看出,vue实例

前言

本篇是分析new vue()过程相关的流程,主要了解如下两点:

  • new vue()整体处理流程
  • 挂载处理流程

整体逻辑

使用vue构造函数创建vue实例,具体的处理逻辑如下图:

这里写图片描述

从上图中可以看出,vue实例的具体处理是定义相关的内部属性,处理实例选项props、data、methods、computed、watch,以及beforecreate、created生命周期函数的执行。

这里每一个选项都有不少实现逻辑,这里先不展开,之后会有专门的文章细究。

实际上面逻辑流程中还存在一些细节点,例如$options的差异处理等,这些需要针对特定的场景来具体聊才能更深的理解,这里先暂时不展开。

el或$mount挂载

对于挂载的处理是在_init即vue()构造函数中调用的,具体的处理就是调用$mount方法,如下:

// el就是挂载点
if (vm.$options.el) vm.$mount(vm.$options.el);

而$mount是定义在vue原型上的实例方法,这也解释了vue挂载的两种方式:

  • el形式,例如: new vue({ el: ‘#app’})
  • $mount形式,例如: new vue().$mount(‘#app’)

$mount方法中具体处理如下:

这里写图片描述

从上面可以得出知晓下面几个结论:

  • el和$mount两种方式挂载的缘由,实际上el本质上还是调用$mount方法
  • 若render函数存在,源码中会将template/el 转换为render函数
  • 挂载点应该是普通的html元素,而不应该是html或body这种特殊的
  • 如果template不存在,实际上会获取包含挂载点在内的outerhtml部分作为template

$mount内部是调用mountcomponent函数,该函数的具体处理是什么呢?具体处理逻辑如下图:

这里写图片描述

从上图逻辑处理中可知:

  • mountcomponent中实际上处理beforemount、mounted
  • 每个vue实例都会有相应的watcher实例对应,watcher构造函数中vm._watcher和vm._watchers记录了当前实例和watcher对象集合

全局watcher的具体逻辑

每一个vue实例都对应一个watcher实例,这个watcher就是在挂载阶段创建的,负责当前实例对应的视图渲染。

其主要逻辑如下:

var updatecomponent = function () {
	vm._update(vm._render(), hydrating);
};
new watcher(vm, updatecomponent, noop, {
	before: function before() {
    	if (vm._ismounted || !vm._isdestroyed) {
          callhook(vm, 'beforeupdate');
        }
    }
}, true)

下面是watcher构造函数中涉及到的主要逻辑:

    var watcher = function watcher (
      vm,
      exporfn,
      cb,
      options,
      isrenderwatcher
    ) {
      this.vm = vm;
      if (isrenderwatcher) {
        vm._watcher = this;
      }
      // 收集当前实例所有watcher对象
      vm._watchers.push(this);
      // options主要处理
      if (options) {
        this.computed = !!options.computed;
      } else {
        this.deep = this.user = this.computed = this.sync = false;
      }
      this.dirty = this.computed; // for computed watchers
      this.deps = [];
      this.newdeps = [];
      this.depids = new _set();
      this.newdepids = new _set();
      if (typeof exporfn === 'function') {
        this.getter = exporfn;
      } 
      if (this.computed) {
        this.value = undefined;
        this.dep = new dep();
      } else {
        this.value = this.get();
      }
    };

watcher对象创建的过程中,有几点逻辑需要主要关注:

  • 每一个vue都对应一个watcher实例,该watcher实例负责视图渲染,可以通过_watcher属性得知
	// 是否是渲染watcher对象
   if (isrenderwatcher) {
    vm._watcher = this;
  }
  • 计算属性的标识即computed
  • 基于computed的不同操作,即get方法是否立即执行,非计算属性的直接就调用了this.get()方法

watcher对象中get方法是非常重要的逻辑,其主要逻辑可以归纳如下:

watcher.prototype.get = function get () {
  pushtarget(this);
  var value;
  var vm = this.vm;
  try {
    value = this.getter.call(vm, vm);
  } catch (e) {
  	// 相关代码
  } finally {
  	// 支持watcher对象
    if (this.deep) {
      traverse(value);
    }
    poptarget();
    this.cleanupdeps();
  }
  return value
};

实际通过get方法的逻辑可以得到几个主要逻辑:

  • pushtarget和poptarget的设置
  • 执行对应的getter函数
  • watch deep选项的支持逻辑

对于全局watcher,这里的getter函数就是执行其对应的render函数渲染出来视图。而watch deep选项则是vue watch api的使用内容,这里暂不展开。

实际上最主要的就是pushtarget相关的逻辑了,实际上这涉及到一个非常重要的属性dep.target。

dep.target

dep.target,该属性是依赖收集关键点之一。

通过vue源码知道,dep.target的改变途径只有两个,即pushtarget和poptarget。

    function pushtarget(target) {
      if (dep.target) targetstack.push(dep.target);
      dep.target = target;
    }
    function poptarget() {
      dep.target = targetstack.pop();
    }

而vue data中每个属性的获取都会检验dep.target是否存在。

只有dep.target存在下才会去调用dep.depend()方法来做相关的处理,而pushtarget和poptarget就是关键了。

vue源码中调用pushtarget函数实际上就4处:

  • handleerror:处理错误情况的方法
  • callhook:生命周期函数调用
  • watcher.prototype.get:watcher对象get实例方法
  • getdata:初始化data时当data为函数会调用getdata方法

从上面的pushtarget函数的逻辑可知会传递target参数,pushtarget会保存当前dep.target到数组(模拟栈结构)中。

上面4处调用pushtarget只有一处传递的非空的target,即watcher.prototype.get。

对于dep.target需要注意的是:

  • dep.target只有三种值:null、undefined、watcher对象
  • pushtarget和poptarget是改变dep.target的唯一途径

而通过pushtarget可以知道targetstack只会保存watcher对象。通过vue源码可知pushtarget和poptarget总是一起搭配出现的,实际上主要作用就是:

在使用pushtarget的地方临时修改dep.target的值,已满足相关条件

明确下这里的相关条件,实际上通过源码中dep.target使用位置来判断是可以清晰得到结论的,即:

dep.target判断操作共有3处:

  • definereactive的get函数中
  • watcher.prototype.depend
  • dep.prototype.depend

从上面可知dep.target是为watcher对象服务的,实际上通过vue响应式原理的描述(dep与watcher是相关关联的从而构成响应式非常重要的一部分),也可以证明dep.target指向watcher对象。

watcher.prototype.get是唯一传递了非空target了,而dep.target值也可能为null、undefined。而=非watcher之外的dep.target的操作都将dep.target临时设置为undefined,是为了避免执行涉及到watcher相关逻辑。

结合整体代码可知:

当执行get函数时,dep.target始终是对应的watcher对象

总结

通过对vue实例创建过程的整体分析,可知其主要的处理逻辑:

  • 执行vue构造函数,开始初始化工作
  • 创建相关实例属性,例如options、uid
  • 相关选项初始化工作,例如状态(data选项、computed、methods等)、事件相关等
  • 执行beforecreate、created生命周期函数
  • el和$mount两种挂载背后的处理

$mount -> mountcomponent -> new watcher() -> _render()执行渲染视图

每一个vue实例都对应一个watcher实例,该watcher实例是用于控制视图渲染的

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com