本篇文章不會具體分析不少每一個方法內部具體邏輯,只爲了研究一下瀏覽器加載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