Vue 動態元件 & 非同步元件原理

Yzz發表於2019-05-05

動態元件 & 非同步元件的存在,使得我們更方便地控制首屏程式碼的體積,加快載入速度。

拋開具體細節不談,一個普通 Vue 元件從建立到展現在頁面裡,主要經歷了以下流程:

// 元件 Object
{
    template: '<div>I am async!</div>'
}
// 經過 compileToFunctions 得到對應的 render function 
with(this) {
    return _c('div', [_v("I am async!")])
}
// 在經過 render 得到 Vnode 再 update 成為真實DOM
複製程式碼

動態元件&非同步元件與之有什麼區別呢?

主要區別在於 rendercreateComponent 這一步,舉例。

// 元件
Vue.component('example', {
    template: '<div>I am async!</div>'
})
複製程式碼

普通元件在 createComponent 時,會依據開發者自定義的 options,利用 Vue.extend 生成對應的建構函式,從而得到對應的 Vnode 。而一個非同步元件

// 非同步元件
Vue.component('async-example', function (resolve, reject) {
    // 利用 setTimeout 模擬請求
    setTimeout(function () {
        // 向 `resolve` 回撥傳遞元件定義
        resolve({
            template: '<div>I am async!</div>'
        })
    }, 1000)
})
複製程式碼

則是要經過一系列處理,具體過程如下

在原始碼的 create-component

// async component
let asyncFactory
if (isUndef(Ctor.cid)) {
    asyncFactory = Ctor
    Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
    if (Ctor === undefined) {
        // return a placeholder node for async component, which is rendered
        // as a comment node but preserves all the raw information for the node.
        // the information will be used for async server-rendering and hydration.
        return createAsyncPlaceholder(
            asyncFactory,
            data,
            context,
            children,
            tag
        )
    }
}
複製程式碼

首先 Ctor 就與之前不同,這裡為一個 function

function (resolve, reject) {
    // 利用 setTimeout 模擬請求
    setTimeout(function () {
        // 向 `resolve` 回撥傳遞元件定義
        resolve({
            template: '<div>I am async!</div>'
        })
    }, 1000)
}
複製程式碼

之後呼叫 resolveAsyncComponent(asyncFactory, baseCtor, context)

resolveAsyncComponent 在原始碼的 resolveAsyncComponent

resolveAsyncComponent 的主要功能是定義 Ctor 所需要的 resolvereject 函式

// factory 為 Ctor
factory(resolve, reject)
複製程式碼

resolve 函式為例

const resolve = once((res: Object | Class<Component>) => {
    // 快取 resolved
    factory.resolved = ensureCtor(res, baseCtor)
    // 強制渲染
    if (!sync) {
    	forceRender(true)
    }
})
複製程式碼

once 字面理解,就是隻呼叫一次。當 CtorsetTimeout 結束時呼叫。

ensureCtor 就是 Vue.extend 的封裝以適應不同場景,所以 resolve 函式的主要功能就是在非同步完成時,將得到的 Ctor 轉化為建構函式,快取在 factory.resolved 中。

之後利用 forceRender(true) 強制重新 render,由於之前快取了 factory.resolvedresolveAsyncComponent 函式就直接返回了元件的建構函式。

if (isDef(factory.resolved)) {
    return factory.resolved
}
複製程式碼

之後就與普通元件一致了。

相關文章