iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 14
0
Modern Web

技術在走,Vue.js 要有系列 第 14

|D14| 從原始碼看 Vue 元件化 (7) - 異步元件

  • 分享至 

  • xImage
  •  

使用異步元件可以在需要時再載入,Vue.component 第二個參數是傳入元件物件,若要使用異步元件,有三種不同的傳入參數

  • 工廠函數

    Vue.component('async-example', function (resolve, reject) {
      setTimeout(function () {
        resolve({
          template: '<div>I am async!</div>'
        })
      }, 1000)
    })
    
  • Promise

    Vue.component(
      'async-webpack-example',
      () => import('./my-async-component')
    )
    
  • 物件
    物件定義方式的功能比較多,還能定義在異步元件載入前的 loading 元件,設定 delay 幾秒後顯示異步元件

    const LoadingComponent = {
      template: '<div>Loading...</div>'
    };
    const ErrorComponent = {
      template: '<div>Error!!!</div>'
    };
    
    const AsyncComponent = () => ({
      component: import('./MyComponent.vue'),
      loading: LoadingComponent,
      error: ErrorComponent,
      delay: 200,
      timeout: 3000
    })
    

resolveAsyncComponent 處理上面三種異步元件的創建方式

// src/core/vdom/helpers/resolve-async-component.js


export function resolveAsyncComponent (
  factory: Function,
  baseCtor: Class<Component>,
  context: Component
): Class<Component> | void {
  if (isTrue(factory.error) && isDef(factory.errorComp)) {
    return factory.errorComp
  }

  if (isDef(factory.resolved)) {
    return factory.resolved
  }

  if (isTrue(factory.loading) && isDef(factory.loadingComp)) {
    return factory.loadingComp
  }

  if (isDef(factory.contexts)) {
    factory.contexts.push(context)
  } else {
    const contexts = factory.contexts = [context]
    let sync = true

    const forceRender = () => {
      for (let i = 0, l = contexts.length; i < l; i++) {
        contexts[i].$forceUpdate()
      }
    }

    const resolve = once((res: Object | Class<Component>) => {
      factory.resolved = ensureCtor(res, baseCtor)
      if (!sync) {
        forceRender()
      }
    })

    const reject = once(reason => {
      process.env.NODE_ENV !== 'production' && warn(
        `Failed to resolve async component: ${String(factory)}` +
        (reason ? `\nReason: ${reason}` : '')
      )
      if (isDef(factory.errorComp)) {
        factory.error = true
        forceRender()
      }
    })

    const res = factory(resolve, reject)

    if (isObject(res)) {
      if (typeof res.then === 'function') {
        // () => Promise
        if (isUndef(factory.resolved)) {
          res.then(resolve, reject)
        }
      } else if (isDef(res.component) && typeof res.component.then === 'function') {
        res.component.then(resolve, reject)

        if (isDef(res.error)) {
          factory.errorComp = ensureCtor(res.error, baseCtor)
        }

        if (isDef(res.loading)) {
          factory.loadingComp = ensureCtor(res.loading, baseCtor)
          if (res.delay === 0) {
            factory.loading = true
          } else {
            setTimeout(() => {
              if (isUndef(factory.resolved) && isUndef(factory.error)) {
                factory.loading = true
                forceRender()
              }
            }, res.delay || 200)
          }
        }

        if (isDef(res.timeout)) {
          setTimeout(() => {
            if (isUndef(factory.resolved)) {
              reject(
                process.env.NODE_ENV !== 'production'
                  ? `timeout (${res.timeout}ms)`
                  : null
              )
            }
          }, res.timeout)
        }
      }
    }

    sync = false
    return factory.loading
      ? factory.loadingComp
      : factory.resolved
  }
}

Promise

Vue.component(
  'async-webpack-example',
  () => import('./my-async-component')
)

執行完 const res = factory(resolve, reject),返回值就是 import('./my-async-component') 的返回值(Promis 物件),接著滿足 if 條件進入執行,當異步元件 loading 成功執行 resolve,loading 失敗執行 reject

// src/core/vdom/helpers/resolve-async-component.js

if (isUndef(factory.resolved)) {
  res.then(resolve, reject)
}

上一篇
|D13| 從原始碼看 Vue 元件化 (6) - 元件註冊
下一篇
|D15| 從原始碼看 Vue 響應式原理 (1) - JS Object.defineProperty
系列文
技術在走,Vue.js 要有30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言