从源码来看VUE的执行流程

  1. 进入页面
  2. Vue初始化
    执行new Vue()进入到Vue的构造函数
  3. _init()方法
    初始化绑定事件和生命周期钩子
  4. 调用beforeCreate这个钩子函数
    在这个钩子函数中还没有初始化数据,所以在这个钩子函数中一般不进行操作
  5. 紧接着通过initState函数调用initProps,initMethods,initData,initComputed,initWatch
    进行props、methods、data、computed、watch等的初始化
    1.这个过程中已经将props,data数据转换为了响应式数据
    2.将methods挂载到vm(this)上
    3.对initComputed执行new watcher(vm,getter,noop,{lazy: true })再执行defineComputed
    defineComputed的逻辑是Object.defineProperty(vm, computedKey,{ get: createComputedGetter, set: noop}) 对Computed数据绑定get为createComputedGetter
    createComputedGetter:
          var watcher = this._computedWatchers && this._computedWatchers[key];
          if (watcher) {
            if (watcher.dirty) {
              watcher.evaluate();
            }
            if (Dep.target) {
              watcher.depend();
            }
            return watcher.value
          }
    Watcher.prototype.evaluate = function evaluate () {
        this.value = this.get();
        this.dirty = false;
    };
    4.initWatch循环执行createWatcher
    createWatcher执行new Watcher(vm, Watch的key, Watchkey对应的方法, {user: true})
    this.value = this.lazy ? undefined : this.get();

    Watcher根据lazy来判断是否延期执行watcher的get方法
    watcher的get方法是先pushtarget再执行this.getter.call(vm,vm)再执行poptarget()

  6. 调用了created钩子函数
    在这个钩子函数中已经可以拿到数据,而且可以对数据进行修改,我们可以在这个钩子函数中向后端发起请求,异步获取到数据,这个时候修改数据不会调用update函数,也不会触发其他生命周期钩子调用mount函数
  7. 调用$mount函数(不是生命周期的钩子函数)
    在$mount函数中将 template/el 转化成 render 函数,准备渲染
  8. 调用mountComponent
  9. 调用beforeMount钩子
    模板已经编译好了,还没有转为真实DOM挂载到页面,这个钩子函数中也可以请求数据,修改数据,修改也不会触发updata函数,不会触发其他生命周期钩子函数
  10. 定义了updateComponent
    给updateComponent赋值为一个将虚拟DOM转换为真实DOM并挂载到页面上的函数。
    updateComponent = function () {
         vm._update(vm._render(), hydrating);
    }

    render函数的功能(主要是利用createElement函数生成vnode)

    _update最重要的是执行__patch__函数的功能(主要是将vnode转换成dom,渲染在视图中,diff的操作也是在这个方法中)
    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
      const vm: Component = this
      // 页面的挂载点,真实的元素
      const prevEl = vm.$el
      // 老 VNode
      const prevVnode = vm._vnode
      const restoreActiveInstance = setActiveInstance(vm)
      // 新 VNode
      vm._vnode = vnode
      // Vue.prototype.__patch__ is injected in entry points
      // based on the rendering backend used.
      if (!prevVnode) {
        // 老 VNode 不存在,表示首次渲染,即初始化页面时走这里
        vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
      } else {
        // 响应式数据更新时,即更新页面时走这里
        vm.$el = vm.__patch__(prevVnode, vnode)
      }
      restoreActiveInstance()
      // update __vue__ reference
      if (prevEl) {
        prevEl.__vue__ = null
      }
      if (vm.$el) {
        vm.$el.__vue__ = vm
      }
      // if parent is an HOC, update its $el as well
      if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
        vm.$parent.$el = vm.$el
      }
      // updated hook is called by the scheduler to ensure that children are
      // updated in a parent's updated hook.
    }
  11. 初始化Watcher实例(renderwatcher,没有传入的lazy,所以是false,会在实例化的时候就执行Watcher的this.get()方法),
    将updateComponent作为回调函数cb传入Watcher,在Watcher构造器中,判断了cb回调函数是否是函数,如果是函数,赋给this.getter,
  12. 调用this.get()方法
    依赖收集就是发生在这个get方法中, 在get方法中,首先调用pushTarget()方法将这个Watcher实例入栈,并设置Dep.target = Watcher实例(启用依赖收集)。然后调用this.getter(),也就是调用updateComponent这个回调函数,在这个函数中,首先调用_render()方法将虚拟DOM渲染为真实DOM,在这个方法中,触发了c方法,v方法,s方法,会访问到所依赖的数据,触发数据的get属性,然后判断Dep.target是否存在,我们在pushTarget中已经启用了依赖收集,所以这个时候就会通过判断,执行depend方法,调用Watcher的addDep方法,在addDep方法中,首先获取dep的id,然后判断newDepIds数组中是否存在这个id,防止重复收集依赖。如果不存在,将dep.id存到newDepIds数组中,并将这个Watcher实例增加到dep的subs数组中。至此依赖收集完成
    1. 当数据更新后,执行watcher.before()就会立即执行beforeUpdate钩子函数
      Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列queueWatcher,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 (通过watcher的id判断队列中是否存在相同的watcher)watcher 被多次触发,只会被推入到队列中一次
      通过nextTick(flushSchedulerQueue)延迟执行收集到的watcher
    2. 然后执行watcher.run()再执行watcher.get()
      执行watcher.get()的时候,由于watcher存了dep的集合会通过depid进行判断这个dep是否已进行存在,如果存在将不再进行push(watcher)操作
      Vue 的虚拟 dom 机制会重新构建虚拟 dom 与上一次的虚拟 dom 树利用 diff 算法进行对比之后重新渲染,一般不做什么事
    3. 更新成功后执行updated钩子函数
  13. 调用mounted钩子函数
posted @ 2021-12-22 20:06  瑞瑞大人  阅读(259)  评论(0编辑  收藏  举报