new Vue 發生了什麼
new
關鍵字表明實例化一個對象, 而Vue
其實是一個類, 源碼位置是/src/core/instance/index.js。html
在 new Vue()
以後。 Vue 會調用 _init
函數進行初始化,也就是這裏的 init 過程,它會初始化生命週期、事件、 props、 methods、 data、 computed 與 watch 等vue
源碼 -> _initnode
export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { const vm: Component = this // a uid vm._uid = uid++ let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed vm._isVue = true // merge options if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm } // expose real self vm._self = vm initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } if (vm.$options.el) { vm.$mount(vm.$options.el) } } }
在初始化時,會調用以上_init
中代碼,生命週期就是經過 callHook
調用的
它的定義在 src/core/instance/lifecycle 中:git
export function callHook (vm: Component, hook: string) { // #7573 disable dep collection when invoking lifecycle hooks pushTarget() const handlers = vm.$options[hook] const info = `${hook} hook` if (handlers) { for (let i = 0, j = handlers.length; i < j; i++) { invokeWithErrorHandling(handlers[i], vm, null, vm, info) } } if (vm._hasHookEvent) { vm.$emit('hook:' + hook) } popTarget() }
callHook
函數的邏輯很簡單,根據傳入的字符串 hook
,去拿到 vm.$options[hook]
對應的回調函數數組,而後遍歷執行,執行的時候把 vm
做爲函數執行的上下文。github
callhook
函數的功能就是調用某個生命週期鉤子註冊的全部回調函數。web
初始化最核心的邏輯是這段:vue-router
beforeCreate
和 created
函數都是在實例化 Vue
的階段,在 _init
方法中執行的,它的定義在 src/core/instance/init.js 中:segmentfault
beforeCreate
調用的時候,是獲取不到 props
或者 data
中的數據的,由於這些數據的初始化都在 initState
中。後端
能夠看到 beforeCreate 和 created 的鉤子調用是在 initState 的先後,initState 的做用是初始化 props、data、methods、watch、computed 等屬性,以後咱們會詳細分析。那麼顯然 beforeCreate 的鉤子函數中就不能獲取到 props、data 中定義的值,也不能調用 methods 中定義的函數。在這倆個鉤子函數執行的時候,並無渲染 DOM,因此咱們也不可以訪問 DOM,通常來講,若是組件在加載的時候須要和後端有交互,放在這倆個鉤子函數執行均可以,若是是須要訪問 props、data 等數據的話,就須要使用 created 鉤子函數。
--> 此段來自Vue.js 技術揭祕api
接下來會執行這裏的掛載函數mountComponent
beforeMount
就是在掛載前執行的,而後開始建立 VDOM 並替換成真實 DOM,最後執行 mounted 鉤子。
這裏會有個判斷邏輯,若是是外部 new Vue({})
的話,不會存在 $vnode
,因此直接執行 mounted 鉤子了。若是有子組件的話,會遞歸掛載子組件,只有當全部子組件所有掛載完畢,纔會執行根組件的掛載鉤子。
對照生命週期圖,咱們看看在beforeMount
鉤子和mounted
鉤子之間的 Create vm.$el and replace "el" width it
具體都有作了什麼:
初始化以後調用 $mount 會掛載組件,若是是運行時編譯,即不存在 render function 可是存在
template 的狀況,須要進行「編譯」步驟。
Vue 實例掛載如何實現
Vue 中咱們是經過$mount
實例方法去掛載vm
的,$mount
方法在多個文件中都有定義,如 src/platform/web/entry-runtime-with-compiler.js、 src/platform/web/runtime/index.js、 src/platform/weex/runtime/index.js。由於$mount
這個方法的實現是和平臺、構建方式都相關的。
Virtual DOM
就是用一個原生的 JS 對象去描述一個 DOM 節點,因此它比建立一個 DOM 的代價要小不少。在 Vue.js 中,Virtual DOM 是用 VNode
這麼一個 Class 去描述,它是定義在 src/core/vdom/vnode.js 中的。
Virtual DOM
其實就是一棵以 JavaScript 對象(VNode
節點)做爲基礎的樹,用對象屬性來描述節點,實際上它只是一層對真實 DOM 的抽象。最終能夠經過一系列操做使這棵樹映射到真實環境上。因爲 Virtual DOM
是以 JavaScript 對象爲基礎而不依賴真實平臺環境,因此使它具備了跨平臺的能力,好比說瀏覽器平臺
、Weex
、Node
等。
實現 Virtual DOM 下的一個 VNode 節點
// VNode 就是一個 JavaScript 對象,用 JavaScript 對象的屬性來描述當前節點的一些狀態, // 用 VNode 節點的形式來模擬一棵 Virtual DOM 樹。 class VNode { constructor(tag, data, children, text, elm, context, componentOptions, asyncFactory) { // 當前節點的標籤名 this.tag = tag // String // 當前節點的一些數據信息, 好比props,attrs等數據 this.data = data // VNodeData // 當前節點的子節點,是一個數組 this.children = children // Array<VNode> // 當前節點的文本 this.text = text // String // 當前虛擬節點對應的真實dom節點 this.elm = elm // Node this.ns = undefined // String | Void // rendered in this component's scope this.context = context // Component | Void // real context vm for functional nodes this.fnContext = undefined // Component | void // for SSR caching this.fnOptions = undefined // functional scope id support this.fnScopeId = undefined this.key = data && data.key // String | Number | Void this.componentOptions = componentOptions // VNodeComponentOptions | Void this.componentInstance = undefined // component instance // Component placeholder node this.parent = undefined // VNode | Void // strictly internal // contains raw HTML? (server only) this.raw = false // boolean // hoisted static node this.isStatic = false // boolean // necessary for enter transition check this.isRootInsert = true // boolean // empty comment placeholder this.isComment = false // boolean // is a cloned node ? this.isCloned = false // boolean // is a v-once node ? this.isOnce = false // boolean // aysync component factory function this.asyncFactory = asyncFactory // Function this.asyncMeta = undefined // Oject | void } get child() { return this.componentInstance } } // 對VNode進一步封裝,實現一些產生經常使用VNode方法 // 建立空節點 const createEmptyVNode = (text) => { const node = new VNode() node.text = text node.isComment = true return node } // 建立文本節點 function createTextVNode (val) { return new VNode(undefined, undefined, undefined, String(val)) } // 克隆一個VNode節點 // optimized shallow clone // used for static nodes and slot nodes because they may be reused across // multiple renders, cloning them avoids errors when DOM manipulations rely // on their elm reference function cloneVNode(vnode) { const cloned = new VNode( vnode.tag, vnode.data, // clone children array to avoid mutating original in case of cloning a child vnode.children && vnode.children.slice() vnode.text, vnode.elm, vnode.context, vnode.componentOptions, vnode.asyncFactory ) cloned.ns = vnode.ns cloned.isStatic = vnode.isStatic cloned.key = vnode.key cloned.isComment = vnode.isComment cloned.fnContext = vnode.fnContext cloned.fnOptions = vnode.fnOptions cloned.fnScopeId = vnode.fnScopeId cloned.asyncMeta = vnode.asyncMeta cloned.isCloned = true return cloned }
若是咱們有這樣一個vue組件
<template> <span class="demo" v-show="isShow"> This is a span. </span> </template>
轉化成render函數描述的js代碼形式就是這樣的:
相關基礎看這篇文章 -> Vue 路由知識點概括總結
Vue 通用的插件註冊原理
Vue 從它的設計上就是一個漸進式 JavaScript 框架,它自己的核心是解決視圖渲染的問題,其它的能力就經過插件的方式來解決。
如何註冊插件 --> Vue.use
: Vue 提供了 Vue.use 的全局 API 來註冊這些插件
vue/src/core/global-api/use.js
/* @flow */ import { toArray } from '../util/index' export function initUse (Vue: GlobalAPI) { // Vue.use 接受一個 plugin 參數 Vue.use = function (plugin: Function | Object) { // 而且維護了一個 _installedPlugins 數組,它存儲全部註冊過的 plugin const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) if (installedPlugins.indexOf(plugin) > -1) { return this } // additional parameters const args = toArray(arguments, 1) args.unshift(this) // 判斷 plugin 有沒有定義 install 方法 if (typeof plugin.install === 'function') { // 若是有的話則調用該方法,而且該方法執行的第一個參數是 Vue plugin.install.apply(plugin, args) } else if (typeof plugin === 'function') { plugin.apply(null, args) } // 最後把 plugin 存儲到 installedPlugins 中 installedPlugins.push(plugin) return this } } // 能夠看到 Vue 提供的插件註冊機制很簡單, // 每一個插件都須要實現一個靜態的 install 方法, // 當咱們執行 Vue.use 註冊插件的時候,就會執行這個 install 方法, // 而且在這個 install 方法的第一個參數咱們能夠拿到 Vue 對象, // 這樣的好處就是做爲插件的編寫方不須要再額外去import Vue 了
咱們知道, 當vue庫文件加載完後,vue的初始化中已有這個東西:
Vue.options={ components:{ KeepAlive:Object, Transition:Object, TransitionGroup:Object }, directives:{ show:Object, model:Object }, filter:{}, _base:function Vue$3(options){...} }
這些都是vue庫內置的組件和指令,當執行Vue.component、Vue.directive、Vue.filter時就是在對這些內置組件、指令、過濾器進行擴充,因此:
var child=Vue.component('child',{ template:'<div>child</div>', props:['name'] })
執行完後
Vue.options.components={ KeepAlive:Object, Transition:Object, TransitionGroup:Object, child:function VueComponent(options) }
https://yuchengkai.cn/docs/fr...
https://ustbhuangyi.github.io...
vue中SFC文件解析爲SFCDescriptor的流程
Vue源碼分析(11)--實例分析component,props,slot
vue變化偵測原理