摘要

引入一个专门的 API 来定义异步组件。

基本范例

  1. import { defineAsyncComponent } from "vue"
  2. // simple usage
  3. const AsyncFoo = defineAsyncComponent(() => import("./Foo.vue"))
  4. // with options
  5. const AsyncFooWithOptions = defineAsyncComponent({
  6. loader: () => import("./Foo.vue"),
  7. loadingComponent: LoadingComponent,
  8. errorComponent: ErrorComponent,
  9. delay: 200,
  10. timeout: 3000
  11. })

动机

根据 RFC-0008:渲染函数的 API 变化中引入的变化,在 Vue3 中,普通函数现在被视为函数式组件。异步组件现在必须通过 API 方法明确定义。

具体设计

简单的用法

  1. import { defineAsyncComponent } from "vue"
  2. // simple usage
  3. const AsyncFoo = defineAsyncComponent(() => import("./Foo.vue"))

definedAsyncComponent 可以接受一个 loader 函数,该函数返回一个解析为真实组件的 Promise。

  • 如果 resolved 的值是一个 ES 模块,该模块的 default 导出将被自动用作组件。
  • 与 2.x 的区别:请注意,loader 函数不再像 2.x 中那样接收 resolvereject 参数 —— 必须始终返回一个 Promise。
    对于在 loader 函数中依赖自定义 resolve 和 reject 的代码,转换很简单: ```javascript // before const Foo = (resolve, reject) => { // }

// after const Foo = defineAsyncComponent(() => new Promise((resolve, reject) => { // }))

  1. <a name="KcFTw"></a>
  2. ## 选项的用法
  3. ```javascript
  4. import { defineAsyncComponent } from "vue"
  5. const AsyncFooWithOptions = defineAsyncComponent({
  6. loader: () => import("./Foo.vue"),
  7. loadingComponent: LoadingComponent,
  8. errorComponent: ErrorComponent,
  9. delay: 100, // default: 200
  10. timeout: 3000, // default: Infinity
  11. suspensible: false, // default: true
  12. onError(error, retry, fail, attempts) {
  13. if (error.message.match(/fetch/) && attempts <= 3) {
  14. retry()
  15. } else {
  16. fail()
  17. }
  18. }
  19. })
  • delaytime 选项的工作方式和 2.x 中一样

与 2.x 的区别:

  • component 选项被新的 loader 选项替换了,它接受与简单用法中相同的 loader 函数。
    在 2.x 中,一个带有选项的异步组件被定义为:

    1. () => ({
    2. component: Promise<Component>
    3. // ...other options
    4. })

    而到了 3.x 中是这样:

    1. defineAsyncComponent({
    2. loader: () => Promise<Component>
    3. // ...other options
    4. })
  • 2.x 的 loadererror 选项分别被重新命名为 loadingComponent 和 errorComponent,含义更加清晰。

重试控制

新的 onError 选项提供了一个钩子,在 loader 出错的情况下执行自定义的重试行为:

  1. const Foo = defineAsyncComponent({
  2. // ...
  3. onError(error, retry, fail, attempts) {
  4. if (error.message.match(/fetch/) && attempts <= 3) {
  5. // retry on fetch errors, 3 max attempts
  6. retry()
  7. } else {
  8. fail()
  9. }
  10. }
  11. })

注意,retry/fail 就像 promise 的 resolve/reject 一样:必须调用其中之一才能继续处理错误。

与 Suspense 一起使用

在 3.x 中的异步组件默认是可以 suspensible(挂起) 的。这意味着如果它在父元素链中有一个 ,它将被视为该 的一个异步依赖。在这种情况下,加载状态将由 控制,而组件自身的 loadingerrordelaytimeout 选项将被忽略。

异步组件可以通过在选项中指定 suspensible: false 来选择退出 Suspense 的控制,让组件始终控制自己的 loading 状态。

缺点

N/A

备选方案

N/A

采纳策略

  • 语法转换是机械的,可以空过 codemod 进行。挑战在于确定哪些普通函数应该被视为异步组件。可以使用一些基本的启发式方法:
    • 返回动态 import 调用 .vue 文件的箭头函数。
    • 箭头函数返回一个对象,其 component 属性是一个动态的 import 调用。

注意,这可能不包括 100% 的现有情况。

  • 在 compat 构建中,可以检查函数式组件的返回值并警告传统的异步组件使用方式。这应该涵盖所有基于 Promise 的用例。
  • 唯一不能轻易检测的情况是 2.x 的异步组件使用手动 resolve/reject 而不是返回 promise。这种情况需要手动升级,但应该比较少。

没有解决的问题

N/A