defineAsyncComponent 是 Vue 3 引入的一个 API,用于定义和加载异步组件。它的主要作用是实现懒加载(Lazy Loading),即在实际需要时才加载组件,优化页面的性能和提升用户体验,尤其适用于大型应用和单页面应用(SPA)。
在 Vue 3 中,异步组件的使用变得更加简单和灵活,defineAsyncComponent 提供了比传统的 Vue.component 更加丰富的配置选项和更强的功能。
1. 基本使用
defineAsyncComponent 通过接受一个函数,该函数返回一个 Promise,来异步加载组件。你可以将 import() 语法直接传递给它,来实现懒加载。
import { defineAsyncComponent } from 'vue'const AsyncComponent = defineAsyncComponent(() =>import('./MyComponent.vue')
)export default {components: {AsyncComponent}
}
在上面的示例中,MyComponent.vue 只有在被渲染时才会异步加载。
2. 进阶用法
除了基本的懒加载,defineAsyncComponent 还提供了更多的配置选项,可以用来处理加载过程中的不同情况,比如加载中的提示、加载失败时的错误提示,以及控制超时等。
配置选项:
loader
:异步加载组件的函数。通常是使用 import() 来返回一个 Promise。
loadingComponent
:(可选)组件加载过程中显示的组件,通常用于显示一个加载中的指示器(例如进度条或加载图标)。
errorComponent
:(可选)如果加载失败,显示的组件。可以用来提示用户组件加载失败或网络问题。
delay
:(可选)加载延迟时间,单位为毫秒。即在组件开始加载后延迟多少毫秒才显示 loadingComponent,可以避免频繁地显示加载提示。
timeout
:(可选)加载超时时间,单位为毫秒。如果组件加载超过这个时间,errorComponent 将会显示出来,提示用户加载失败。
import { defineAsyncComponent } from 'vue'
import LoadingSpinner from './LoadingSpinner.vue'
import ErrorComponent from './ErrorComponent.vue'const AsyncComponent = defineAsyncComponent({loader: () => import('./MyComponent.vue'),loadingComponent: LoadingSpinner, // 加载中显示的组件errorComponent: ErrorComponent, // 加载失败显示的组件delay: 200, // 延迟200ms后才显示加载组件timeout: 3000 // 超过3秒后显示加载失败组件
})export default {components: {AsyncComponent}
}
在上述代码中,AsyncComponent 是一个异步加载的组件,当该组件加载时,先显示 LoadingSpinner,如果加载失败则显示 ErrorComponent,并且如果加载超时超过 3 秒钟,则也会显示错误组件。
- 参数说明
- loader:
类型:() => Promise<*>
必须提供一个返回 Promise 的函数,通常用 import() 来异步加载组件。
- loadingComponent:
类型:Component | undefined
可选。在组件加载过程中显示的组件。一般是一个加载动画或进度条。
- errorComponent:
类型:Component | undefined
可选。如果组件加载失败时,显示的组件。通常是一个错误提示,告知用户加载失败。
- delay:
类型:number | undefined
可选。加载组件时,延迟显示 loadingComponent 的时间,单位为毫秒。如果加载过程很短,不希望频繁显示加载指示器,可以设置较长的延迟时间。
- timeout:
类型:number | undefined
可选。超时时间,单位为毫秒。如果组件加载时间超过这个值,会显示 errorComponent,提示用户加载失败。
4. 使用场景
- 提高性能:异步加载组件可以减少初次加载时需要下载的资源量,使页面加载速度更快。
- 按需加载:例如在多路由应用中,可以为每个路由配置异步加载的组件,避免一次性加载所有页面的资源。
- 分离大组件:对于一些大型的功能模块,可以通过 defineAsyncComponent
将其拆分成多个小的异步加载组件,减少应用的初始加载时间。 - 错误处理和用户提示:通过配置 errorComponent 和 loadingComponent,可以提高应用的鲁棒性和用户体验。
- 与 Vue Router 的结合使用
在 Vue Router 中,可以通过 defineAsyncComponent 来实现路由组件的懒加载,从而减少路由切换时的延迟。
import { createRouter, createWebHistory } from 'vue-router'
import { defineAsyncComponent } from 'vue'const routes = [{path: '/home',component: defineAsyncComponent(() =>import('./views/Home.vue'))},{path: '/about',component: defineAsyncComponent(() =>import('./views/About.vue'))}
]const router = createRouter({history: createWebHistory(),routes
})export default router
在这个例子中,当用户访问 /home 或 /about 路径时,Vue 会根据路由懒加载相关的组件,而不是一次性加载所有的视图组件。
6. 我们在axios请求拦截器或者之前的阶段使用pinia中会出现报错问题
Uncaught Error: [🍍]: “getActivePinia()” was called but there was no
active Pinia. Are you trying to use a store before calling"app.use(pinia)"?
表明你的代码中使用了 Pinia(Vue 的状态管理库),但在调用 getActivePinia() 时没有正确地初始化 Pinia,或者没有在应用中使用 app.use(pinia) 来注册 Pinia 插件。
import axios from "axios";
import { getAppStore } from "../store";
import { showDialog } from "vant";const sender = axios.create({baseURL: "api",headers: {"Content-Type": "application/json",},
});const appStore = getAppStore();// 添加请求拦截器
// https://axios-http.com/zh/docs/interceptors
sender.interceptors.request.use(function (config) {// console.log('请求拦截器')// 统一添加 token 请求头config.headers["Authorization"] = appStore.token;return config;},function (error) {// console.log('请求拦截器 error: ', error)// 对请求错误做些什么return Promise.reject(error);}
);// 添加响应拦截器
sender.interceptors.response.use(function (response) {// console.log('响应拦截器', response)// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么appStore.loading = false;if (response.headers.token) {appStore.token = response.headers.token;// console.log('update token '+response.headers.token)}return response.data;},function (error) {// console.log('响应拦截器 error: ', error)// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么appStore.loading = false;if (error.status === 401) {appStore.isLogin = false;appStore.token = "";appStore.isShowLoginPopup = true;const router = appStore.router;router.push("/");}if (error.status === 422) {showDialog({teleport: "#main",className: "global-dialog",showConfirmButton: false,closeOnClickOverlay: true,message: error.response.data.message,});}return Promise.reject(error);}
);export const send = sender;
解决方法
异步导入文件或者组件,就可以在页面加载完毕后才调用pinia。
把这种引入方式改为下面那种就可以解决了
import LeftPanel from './components/LeftPanel.vue'
const LeftPanel = defineAsyncComponent(() => import("./components/LeftPanel.vue")
)