[Vue 源码] Vue 3.2 - 异步组件原理

242 阅读2分钟

代码运行结果

asyncComponent.gif

代码示例

      let asyncComponent = defineAsyncComponent({
        loader: async () => {
          await new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve()
            }, 3000)
          })

          return import('./test.js').then(data => data.default)
        },
        timeout: 2000,
        delay: 1000,

        loadingComponent: {
          render: () => {
            return h('div', 'loading…………')
          }
        },
        errorComponent: {
          render: () => {
            return h('div', 'error…………')
          }
        }
      })

      createApp(asyncComponent).mount('#app')
// ./test.js
const App = {
  setup() {
    return () => h('div', 'App')
  }
}
export default App

第一:首先调用 defineAsyncComponent 函数,并传入 option. 在 函数中初始化状态后,返回一个 defineComponent 定义的组件对象。

const {
  loader,
  loadingComponent,
  errorComponent,
  delay = 200,
  timeout, // undefined = never times out
  suspensible = true,
  onError: userOnError
} = source

let retries = 0

const retry = () => {
  retries++
  pendingRequest = null
  return load()
}

const load = (): Promise<ConcreteComponent> => {}
return defineComponent({
    return () => {
      if (loaded.value && resolvedComp) {
        return createInnerComp(resolvedComp, instance)
      } else if (error.value && errorComponent) {
        return createVNode(errorComponent as ConcreteComponent, {
          error: error.value
        })
      } else if (loadingComponent && !delayed.value) {
        return createVNode(loadingComponent as ConcreteComponent)
      }
    }
  }
}) as T

第二:执行,createApp(asyncComponent).mount('#app'),执行 asyncComponent 组件对象的 setup 函数:

  • 初始化 loaded, error, delay 响应式状态。
  • 调用 load 函数,调用我们传入的 loader 异步函数。异步函数中异步 resolve 挂起微任务队列。继续执行同步任务。
  • 同步任务继续执行,判断当前 loaded 状态为 false, 然后 delay 为 true, 白屏渲染。
      return () => {
        if (loaded.value && resolvedComp) {
          return createInnerComp(resolvedComp, instance)
        } else if (error.value && errorComponent) {
          return createVNode(errorComponent as ConcreteComponent, {
            error: error.value
          })
        } else if (loadingComponent && !delayed.value) {
          return createVNode(loadingComponent as ConcreteComponent)
        }
      }
  • delay 一秒之后,将 delay 置为 false, delay 响应式对象触发组件更新,重新执行 render 函数,然后 delay 为 false 和 loadingComponent, loadingComponent渲染。
      if (delay) {
        setTimeout(() => {
          delayed.value = false
        }, delay)
      }
  • timout 到第三秒的时候,这时 loaded 仍然为 false,但是触发超时,error 响应式对象触发组件更新,触发 onError 函数,errorComponent渲染

  • 等到第三秒的时候,resolve 的微任务队列执行。拿到要渲染的组件对象赋值给 resolvedComp,然后将 loaded 置为 true, loaded 响应式对象触发组件更新, 显示 App 组件。

      return () => {
        if (loaded.value && resolvedComp) {
          return createInnerComp(resolvedComp, instance)
        } else if (error.value && errorComponent) {
          return createVNode(errorComponent as ConcreteComponent, {
            error: error.value
          })
        } else if (loadingComponent && !delayed.value) {
          return createVNode(loadingComponent as ConcreteComponent)
        }
      }

至此异步组件渲染完毕。