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) } renderMixin(Vue)
初始化先執行了 renderMixin 方法, Vue 實例化執行this._init, 執行this.init方法中有initRender()node
installRenderHelpers( 將一些渲染的工具函數放在Vue 原型上)數組
Vue.prototype.$nextTick = function (fn: Function) { return nextTick(fn, this) }
仔細看這個函數, 在Vue中的官方文檔上這樣解釋promise
Vue 異步執行 DOM 更新。只要觀察到數據變化,Vue 將開啓一個隊列,並緩衝在同一事件循環中發生的全部數據改變。若是同一個 watcher 被屢次觸發,只會被推入到隊列中一次。這種在緩衝時去除重複數據對於避免沒必要要的計算和 DOM 操做上很是重要。而後,在下一個的事件循環「tick」中,Vue 刷新隊列並執行實際 (已去重的) 工做。Vue 在內部嘗試對異步隊列使用原生的 Promise.then 和MessageChannel,若是執行環境不支持,會採用 setTimeout(fn, 0)代替。app
export function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } // $flow-disable-line if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
Vue.nextTick用於延遲執行一段代碼,它接受2個參數(回調函數和執行回調函數的上下文環境),若是沒有提供回調函數,那麼將返回promise對象。異步
function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
這個flushCallbacks 是執行callbacks裏存儲的全部回調函數。
timerFunc 用來觸發執行回調函數函數
const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true
MutationObserver是一個構造器,接受一個callback參數,用來處理節點變化的回調函數,observe方法中options參數characterData:設置true,表示觀察目標數據的改變工具
經過執行 createElement 方法並返回的是 vnode,它是一個虛擬的 Node。優化
vnode = render.call(vm._renderProxy, vm.$createElement)
兼容不傳data的狀況 以及判斷傳入的alwaysNormalize是否爲true
再調用_createElement函數,能夠看到,createElement是對參數作了一些處理之後,將其傳給_createElement函數。this
if (Array.isArray(data) || isPrimitive(data)) { normalizationType = children children = data data = undefined } if (isTrue(alwaysNormalize)) { normalizationType = ALWAYS_NORMALIZE }
若是data未定義(undefined或者null)或者是data的__ob__已經被observed,上面綁定了Oberver對象 就建立一個空節點prototype
if (isDef(data) && isDef((data: any).__ob__)) { process.env.NODE_ENV !== 'production' && warn( `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` + 'Always create fresh vnode data objects in each render!', context ) return createEmptyVNode() }
主要完成的功能是判斷children中的元素是否是數組,若是是的話,就遞歸調用數組,並將每一個元素保存在數組中返回。
export function normalizeChildren (children: any): ?Array<VNode> { return isPrimitive(children) ? [createTextVNode(children)] : Array.isArray(children) ? normalizeArrayChildren(children) : undefined }
normalizeArrayChildren 核心代碼
先判斷children中的元素是否是數組,是的話遞歸調用函數。若是第一個和最後一個都是文本節點的話,將其合併,優化。判斷該元素是否是基本類型。若是是,在判斷最後一個結點是否是文本節點,是的話將其與該元素合併爲一個文本節點。不然,把這個基本類型轉換爲文本節點(VNode)最後一種狀況,該元素是一個VNode,先一樣進行優化(合併第一個和最後一個節點),而後判斷該節點的屬性,最後將該節點加入到結果中。
if (Array.isArray(c)) { if (c.length > 0) { c = normalizeArrayChildren(c, `${nestedIndex || ''}_${i}`) // merge adjacent text nodes if (isTextNode(c[0]) && isTextNode(last)) { res[lastIndex] = createTextVNode(last.text + (c[0]: any).text) c.shift() } res.push.apply(res, c) } } else if (isPrimitive(c)) { if (isTextNode(last)) { // merge adjacent text nodes // this is necessary for SSR hydration because text nodes are // essentially merged when rendered to HTML strings res[lastIndex] = createTextVNode(last.text + c) } else if (c !== '') { // convert primitive to vnode res.push(createTextVNode(c)) } } else { if (isTextNode(c) && isTextNode(last)) { // merge adjacent text nodes res[lastIndex] = createTextVNode(last.text + c.text) } else { // default key for nested array children (likely generated by v-for) if (isTrue(children._isVList) && isDef(c.tag) && isUndef(c.key) && isDef(nestedIndex)) { c.key = `__vlist${nestedIndex}_${i}__` } res.push(c) } }