使用 Vue 3 的 defineAsyncComponent
特性可以让我们延迟加载组件。这意味着它们仅在需要时从服务器加载。
什么是defineAsyncComponent
// SOURCE: https://v3.vuejs.org/guide/component-dynamic-async.html
const AsyncComp = defineAsyncComponent(() =>new Promise((resolve, reject) => {resolve({template: '<div>I am async!</div>'})})
)
defineAsyncComponent
接受一个返回Promise的工厂函数。当我们成功地从服务器获取组件时,这个Promise应该会被 resolve
,如果出现错误则会被 reject
。
要使用它,我们必须从Vue中导入它,然后才能在脚本的其余部分中使用它。
我们也可以使用工厂函数中的 import
,轻松地从其他文件中添加Vue组件。
import { defineAsyncComponent } from "vue"
// 简单使用
const LoginPopup = defineAsyncComponent(() => import("./components/LoginPopup.vue"))
这是使用 defineAsyncComponent
的最简单方法,但我们也可以传入一个完整的选项对象,配置几个更高级的参数。
// with options
const AsyncPopup = defineAsyncComponent({ loader: () => import("./LoginPopup.vue"),loadingComponent: LoadingComponent, /* 在加载时显示 */errorComponent: ErrorComponent, /* 显示是否有错误 */delay: 1000, /* 在显示加载组件之前延迟毫秒 */timeout: 3000 /* 这个毫秒之后的超时 */
})
使用defineAsyncComponent延迟加载弹出组件
在本例中,我们将使用一个由单击按钮触发的登录弹出窗口。
每当我们的应用程序加载时,我们不需要我们的应用程序加载此组件,因为只有在用户执行特定操作时才需要它。
所以这就是我们的登录组件的样子,它只是通过用 position: fixed
将屏幕的其余部分涂黑来创建一个弹出窗口,并且有一些输入和一个提交按钮。
<template> <div class="popup"> <div class="content"> <h4> Login to your account </h4> <input type="text" placeholder="Email" /> <input type="password" placeholder="Password" /> <button> Log in </button> </div> </div>
</template>
<script>
</script>
<style scoped> .popup {position: fixed;width: 100%;top: ; left: ;height: 100%;background-color: rgba(, , , 0.2);display: flex;justify-content: center;align-items: center;
}
.content { min-width: 200px; width: 30%; background: #fff; height: 200px; padding: 10px; border-radius: 5px;
}
input[type="text"], input[type="password"] {border: ;outline: ;border-bottom: 1px solid #eee;width: 80%;margin:auto;font-size: 0.5em;
}
button { border: ; margin-top: 50px; background-color:#8e44ad; color: #fff; padding: 5px 10px; font-size: 0.5em;
} </style>

而不是像我们通常那样导入它并将其纳入我们的 components
选项中。
<!-- "Standard" way of doing things -->
<template><button @click="show = true"> Login </button><login-popup v-if="show" />
</template>
<script> import LoginPopup from './components/LoginPopup.vue'
export default {components: { LoginPopup },data() {return {show: false}}
} </script>
我们可以改为使用 defineAsyncComponent
仅在需要时加载它(意味着单击按钮并切换我们的 v-if
)
<!-- Use defineAsyncComponent-->
<template><button @click="show = true"> Login </button><login-popup v-if="show" />
</template>
<script> import { defineAsyncComponent } from 'vue'
export default {components: { "LoginPopup" : defineAsyncComponent(() => import('./components/LoginPopup.vue'))},data() {return {show: false}}
} </script>
虽然这在我们使用我们的应用程序时可能看起来是一样的,但让我们检查元素 > 网络来理解这个小而重要的区别。
如果我们不使用 defineAsyncComponent
,一旦我们的页面加载,我们就会看到我们的应用程序从服务器上获得LoginPopup.vue
。虽然在这个例子中,这可能不是最大的性能问题,但它仍然会减慢加载速度,如果我们有几十个组件这样做,它真的会加起来。

但是,如果我们使用 defineAsyncComponent
查看同一个选项卡,我们会注意到当我们的页面加载时,LoginPopup.vue
不见了,这是因为它还没有加载。

但是一旦我们点击我们的按钮并告诉我们的应用程序显示我们的弹出窗口,这时它就会从服务器加载,我们可以在网络标签中看到它。

这有助于我们实现最佳性能。我们只想在我们的页面初始加载时加载需要的组件。有条件渲染的组件在我们的页面加载时往往是不需要的,所以为什么要让我们的应用程序加载它们呢?
如何使用异步设置功能
无论我们是否使用 defineAsyncComponent
延迟加载,任何具有异步设置功能的组件都必须用 <Suspense>
包装。
简而言之,创建一个异步设置函数是我们的一个选择,可以让我们的组件在渲染前等待一些API调用或其他异步动作。
这是我们具有异步设置的组件。它使用 setTimeout()
模拟 API 调用。
<template> <div class="popup"> <div class="content"><p> Loaded API: {{ article }} </p> <h4> Login to your account </h4> <input type="text" placeholder="Email" /> <input type="password" placeholder="Password" /> <button> Log in </button> </div> </div>
</template>
<script> const getArticleInfo = async () => { // wait 3 seconds to mimic API callawait new Promise(resolve => setTimeout(resolve, 1000));const article = {title: 'My Vue 3 Article',author: 'Matt Maribojoc'}return article
}
export default {async setup() {const article = await getArticleInfo()console.log(article)return {article}}
} </script>
我们可以在有或没有 defineAsyncComponent
的情况下将它导入到我们的组件中
import LoginPopup from './components/LoginPopup.vue'
// OR
const LoginPopup = defineAsyncComponent(() => import("./components/LoginPopup.vue"))
但是如果我们想让它在我们的模板中渲染,我们需要将它包装在一个 Suspense
元素中。这将等待我们的 setup
函数在尝试渲染我们的组件之前解析。
<template><button @click="show = true"> Login </button><Suspense v-if="show"><template #default><login-popup/></template><template #fallback><p> Loading... </p></template></Suspense>
</template>
这就是结果。用户会看到 “正在加载…”,然后在3秒后(我们的setTimeout的硬编码值),我们的组件将渲染。

默认情况下,我们使用 defineAsyncComponent 定义的所有组件都是可暂停的。
这意味着如果一个组件的父链中有 Suspense,它将被视为该 Suspense 的一个异步依赖。我们的组件的加载、错误、延迟和超时选项将被忽略,而是由 Suspense 来处理。
最后的想法
defineAsyncComponent
在创建有几十个组件的大型项目时是有好处的。当我们进入到懒惰加载组件时,我们可以有更快的页面加载时间,改善用户体验,并最终提高你的应用程序的保留率和转换率。
最后
最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。
有需要的小伙伴,可以点击下方卡片领取,无偿分享