vue 源碼探索(二)

前言
通過上一節(jié) vue 源碼探索(一)我們已經(jīng)基本掌握 vue 的目錄結(jié)構(gòu),接下來我們講解數(shù)據(jù)驅(qū)動(dòng)
數(shù)據(jù)驅(qū)動(dòng)
數(shù)據(jù)驅(qū)動(dòng)是指網(wǎng)頁中所見的視圖由結(jié)構(gòu)化數(shù)據(jù)驅(qū)動(dòng)生成,對(duì)DOM的操作不是直接的增刪改,而是通過修改對(duì)應(yīng)數(shù)據(jù)間接操作DOM。優(yōu)勢(shì)是數(shù)據(jù)和視圖解耦,便于維護(hù)。
<div id="app"> {{ message }} </div> let app = new Vue({ el: #app, data: { message: Hello World } }) // Hello World上面示例會(huì)輸出親切的 hello world 字樣。
new vue 發(fā)生了什么?
通過new vue 我們實(shí)例化一個(gè) vue 對(duì)象
// https://github.com/vuejs/vue/blob/dev/src/core/instance/index.js function Vue (options) { if (process.env.NODE_ENV !== production && !(this instanceof Vue) ) { warn(Vue is a constructor and should be called with the `new` keyword) } this._init(options) }然后調(diào)用 this._init 方法設(shè)置對(duì)應(yīng)參數(shù)
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) } ... ... if (vm.$options.el) { vm.$mount(vm.$options.el) }vue 初始化主要就干了幾件事情,合并配置,初始化生命周期,初始化渲染,初始化 data , props, computed, watcher。
小節(jié)
初始化過程主干負(fù)責(zé)基本校驗(yàn),簡(jiǎn)單賦值,通過具體功能函數(shù)執(zhí)行對(duì)應(yīng)功能。最后將 el 進(jìn)行掛載,掛載的目的就是把模板渲染成最終的 DOM。
vue 掛載機(jī)制
通過 compiler 版本的 $mount 實(shí)現(xiàn)分析掛載機(jī)制,部分分析見代碼注釋
// https://github.com/vuejs/vue/blob/dev/src/platforms/web/entry-runtime-with-compiler.js const mount = Vue.prototype.$mount Vue.prototype.$mount = function ( el?: string | Element, hydrating?: boolean ): Component { el = el && query(el) /* el 不能為 body 或者 html 節(jié)點(diǎn) */ if (el === document.body || el === document.documentElement) { process.env.NODE_ENV !== production && warn( `Do not mount Vue to <html> or <body> - mount to normal elements instead.` ) return this } const options = this.$options /** 如果沒有定義 render 方法,則會(huì)把 el 或者 template 字符串轉(zhuǎn)換成 render 方法。 這里我們要牢記,在 Vue 2.0 版本中, 所有 Vue 的組件的渲染最終都需要 render 方法, 無論我們是用單文件 .vue 方式開發(fā)組件, 還是寫了 el 或者 template 屬性, 最終都會(huì)轉(zhuǎn)換成 render 方法, 那么這個(gè)過程是 Vue 的一個(gè)“在線編譯”的過程, 它是調(diào)用 compileToFunctions 方法實(shí)現(xiàn)的, 編譯過程我們之后會(huì)介紹。最后,調(diào)用原先原型上的 $mount 方法掛載。 */ if (!options.render) { let template = options.template if (template) { if (typeof template === string) { if (template.charAt(0) === #) { template = idToTemplate(template) /* istanbul ignore if */ if (process.env.NODE_ENV !== production && !template) { warn( `Template element not found or is empty: ${options.template}`, this ) } } } else if (template.nodeType) { template = template.innerHTML } else { if (process.env.NODE_ENV !== production) { warn(invalid template option: + template, this) } return this } } else if (el) { template = getOuterHTML(el) } if (template) { /* istanbul ignore if */ if (process.env.NODE_ENV !== production && config.performance && mark) { mark(compile) } const { render, staticRenderFns } = compileToFunctions(template, { outputSourceRange: process.env.NODE_ENV !== production, shouldDecodeNewlines, shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, this) options.render = render options.staticRenderFns = staticRenderFns /* istanbul ignore if */ if (process.env.NODE_ENV !== production && config.performance && mark) { mark(compile end) measure(`vue ${this._name} compile`, compile, compile end) } } } return mount.call(this, el, hydrating) })$mount 方法支持傳入 2 個(gè)參數(shù),第一個(gè)是 el,它表示掛載的元素,可以是字符串,也可以是 DOM 對(duì)象,如果是字符串在瀏覽器環(huán)境下會(huì)調(diào)用 query 方法轉(zhuǎn)換成 DOM 對(duì)象的。第二個(gè)參數(shù)是和服務(wù)端渲染相關(guān),在瀏覽器環(huán)境下我們不需要傳第二個(gè)參數(shù)。
$mount 方法實(shí)際上會(huì)去調(diào)用 mountComponent 方法,這個(gè)方法定義在
export function mountComponent ( vm: Component, el: ?Element, hydrating?: boolean ): Component { vm.$el = el if (!vm.$options.render) { vm.$options.render = createEmptyVNode if (process.env.NODE_ENV !== production) { /* istanbul ignore if */ if ((vm.$options.template && vm.$options.template.charAt(0) !== #) || vm.$options.el || el) { warn( You are using the runtime-only build of Vue where the template + compiler is not available. Either pre-compile the templates into + render functions, or use the compiler-included build., vm ) } else { warn( Failed to mount component: template or render function not defined., vm ) } } }mountComponent 核心就是先實(shí)例化一個(gè)渲染W(wǎng)atcher,在它的回調(diào)函數(shù)中會(huì)調(diào)用 updateComponent 方法,在此方法中調(diào)用 vm._render 方法先生成虛擬 Node,最終調(diào)用 vm._update 更新 DOM。
Watcher 在這里起到兩個(gè)作用,一個(gè)是初始化的時(shí)候會(huì)執(zhí)行回調(diào)函數(shù),另一個(gè)是當(dāng) vm 實(shí)例中的監(jiān)測(cè)的數(shù)據(jù)發(fā)生變化的時(shí)候執(zhí)行回調(diào)函數(shù)。
函數(shù)最后判斷為根節(jié)點(diǎn)的時(shí)候設(shè)置 vm._isMounted 為 true, 表示這個(gè)實(shí)例已經(jīng)掛載了,同時(shí)執(zhí)行 mounted 鉤子函數(shù)。 這里注意 vm.$vnode 表示 Vue 實(shí)例的父虛擬 Node,所以它為 Null 則表示當(dāng)前是根 Vue 的實(shí)例。
小節(jié)
mountComponent 方法的邏輯也是非常清晰的,它會(huì)完成整個(gè)渲染工作。
關(guān)注前端支點(diǎn),更多前端知識(shí)等你探索。
掃描二維碼推送至手機(jī)訪問。
版權(quán)聲明:本文由財(cái)神資訊-領(lǐng)先的體育資訊互動(dòng)媒體轉(zhuǎn)載發(fā)布,如需刪除請(qǐng)聯(lián)系。