看一下從 new Vue()開始到頁面看到真實dom都經歷了什麼?

從 new Vue()開始看vue運行流程

本篇文章不會具體分析不少每一個方法內部具體邏輯,只爲了研究一下瀏覽器加載vuejs文件後以及我new Vue後,都調用了哪些方法,這些方法都是作什麼的等等。以便對vue的執行流程有個大體瞭解,方便碰見問題排查是哪一個過程出了問題。

用vue這麼久我是很好奇從vue是怎麼一步步把單文件組件的內容給渲染到空的div下面的,那從main.js入口開始,爲了方便理解,咱們只保留一個App.vue, main.js文件,路由,vuex,插件等等都不引入,在App.vue裏面咱們只定義一個data字段,插值綁定一個。javascript

vue版本:2.5.2

先看下main.js,比較簡單html

import Vue from "vue";
import App from "./App";
Vue.config.productionTip = false;

/* eslint-disable no-new */
new Vue({
  el: "#app",
  render: h => h(App)
});

下面開始分析具體調用了哪些方法vue

分析過程會忽略一些輔助方法等無關的方法調用...

在new Vue以前也就是會作一些初始化的工做,列舉部分重要的代碼java

/*
initMixin給Vue.prototype添加:
_init函數,
...
*/
initMixin(Vue);
/*
stateMixin給Vue.prototype添加:
$data屬性,
$props屬性,
$set函數,
$delete函數,
$watch函數,
...
*/
stateMixin(Vue);
/*
eventsMixin給Vue.prototype添加:
$on函數,
$once函數,
$off函數,
$emit函數,
$watch方法,
...
*/
eventsMixin(Vue);
/*
lifecycleMixin給Vue.prototype添加:
_update方法:私有方法,用於更新dom,其中調用_patch產生跟新後的dom,
$forceUpdate函數,
$destroy函數,
...
*/
lifecycleMixin(Vue);
/*
renderMixin給Vue.prototype添加:
$nextTick函數,
_render函數,
...
*/
renderMixin(Vue);

至此,部分常見的咱們常常看到的初始化工做已經作完,如今咱們知道咱們在vm上用的$開頭的方法都是在一加載vue.js完成後就掛在了Vue.prototype,咱們的vm實例就能夠用這些方法了。
下面當咱們正式開始new Vue()node

//調用this._init()
new Vue(options);
/*
this._init函數
依次調用了跟vm相關的初始化函數:
initLifecycle:給vm掛在一下屬性
  $parent:undefined,
  $root:vm,
  $children:[],
  $refs:{},
  _isMounted:false,
  _isDestoryed:false,
  _watcher:null,
  ...
initEvents:初始化事件.

initRender:主要作了一下事情:
  給vm添加_c函數和$createElement,實際上是createElement別名, 
  給vm添加$attrs和$listeners屬性,$attrs & $listeners are exposed for easier HOC creation

callHook(vm, 'beforeCreate'):此時咱們看到觸發了beforeCreate鉤子,此時的vm有哪些屬性應該一目瞭然了.

initInjections(vm): resolve injections before data/props,咱們能夠看到在初始化inject時尚未data和props

initState:主要進行初始化:
  data:initData(vm),
  props:initProps(vm,opt.props)
  computed:initComputed(vm,opt.computed),
  methods:initMethods(vm,opt.methods),
  watch:initWatch(vm,opt.watch)

initProvide:resolve provide,根據源碼註釋咱們能夠知道,在provide中可使用props和data

callHook(vm, 'created'):此時生命週期created觸發,咱們能訪問data,prop,provide等等
下面最後一步相當重要:
 if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
這裏是判斷咱們是否傳入了el,屬性,傳入了則調用$mount方法掛載內容到el所在節點下
*/
 this._init(options)
//在執行完 this._init() 後進入了最最重要的一步,掛載組件
//程序接着往下走回執行:mountComponent
/*
  函數 mountComponent 主要作了下面這些事情:
  觸發 beforeMount() 鉤子函數,
  聲明 updateComponent 函數,裏面調用了 vm_update(vm._render(),...),這兩個方法做用下面執行到的時候說一下.
  new Watcher(),此時會傳入updateComponent函數,並隨後執行此函數,執行後會發生一些函數執行,我只列舉比較重要的大流程函數:
  Vue._render:執行由vue-loader生成的render函數或者本身寫的render函數,最終返回一個由createComponent(非createPatchFunction內部的)產生的vnode.
  createComponent(非createPatchFunction內部):建立組件虛擬節點,此函數返回一個vnode,表示vue組件的vnode.
  vm._update:接收上面的vnode參數,這裏面會觸發VM.__patch__函數,這個函數裏面最終返回的結果就是咱們在html頁面寫的空的div,可是裏面有了真實的內容,此時頁面能夠看到內容了,
  觸發 mount() 鉤子函數,這個mount鉤子每一個組件實例會在本身的insert hook中調用

*/
mountComponent()

到了這裏,函數moutComponent執行完畢而且返回了vm,
this._init()執行完咱們在頁面就能夠看到內容了vuex

總結

咱們能夠大體總結一下從new Vue開始都大體執行了哪些重要的方法瀏覽器

1.new Vue();
2.Vue.prototype._init();
3.Vue.prototype.$mount();
4.mountComponent();
5.new Watcher();
6.Watcher.prototype.get();
7.updateComponent();
8.Vue.prototype._render();
9.render();
10.createElement()
11.Vue.prototype._update();//這裏面會執行vm.$el=vm.__patch__(),最終根vm的$el就有了真實dom值
12.Vue.prototype.__patch__();//這個應該是最重要的方法了,他返回了真實的dom節點。

vm.__patch__中會產生一個屬於App.vue這個虛擬節點的實例,而後再次調用該實例的_init()方法,而後依次執行步驟2到12,繼而完成app組件的掛載,最終new Vue出來的vm的$el,就是全部的真實dom。app

在開始研究時在瀏覽器一步步調試看執行的過程,在谷歌的調試工具中藉助這個call Stack框架

圖片描述

很方便查看運行過程都調用了哪些函數,在程序運行vm.__patch__后里面涉及到了這倆函數 createElm,createChilren互相調用的邏輯,而後因爲在這以前又調用了幾十個函數,致使調試到最後按F10電腦要卡頓很久才能進入下一步,咱們看到頁面多是一瞬間的事情,可是vue幫咱們作了不少事情,很感謝vue這麼優秀的框架。dom

最後但願你們也去本身嘗試下調試下源碼,你會有新的發現!

相關文章
相關標籤/搜索