本文最后更新于 2024-03-22T23:32:47+00:00
组件高级应用
动态组件
官方文档:https://cn.vuejs.org/guide/essentials/component-basics.html#dynamic-components
基本语法
1 2 3 4 5 6 7 8 9 10 11
| <template> <div> <component :is="comName" /> </div> </template>
<script setup lang="ts"> import Content1 from './Content1' const comName = Content1 </script>
|
使用场景
异步动态组件
官方文档:https://cn.vuejs.org/guide/components/async.html#async-components
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <!-- <template> --> <div> <button @click="handleChange">切换</button>
<component :is="comName.component" /> </div> </template>
<script setup lang="ts"> import { defineAsyncComponent } from 'vue'
const comName = reactive({ name: '', component: '' })
const handleChange = () => { comName.name = comName.name === 'Content1' ? 'Content2' : 'Content1'
comName.component = defineAsyncComponent(() => import(`./${comName.name}.vue`)) } </script>
|
动态组件切换时,生命周期也是正常的销毁、创建,不会保留。
KeepAlive
官方文档:https://cn.vuejs.org/guide/built-ins/keep-alive.html#keepalive
作用:在多个组件间动态切换时缓存被移除的组件实例,不会触发创建、销毁的生命周期。
配合动态组件使用更佳
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <template> <div> <button @click="handleChange">切换</button>
<KeepAlive> <component :is="comName.component" /> </KeepAlive> </div> </template>
<script setup lang="ts"> import { defineAsyncComponent } from 'vue'
const comName = reactive({ name: '', component: '' })
const handleChange = () => { comName.name = comName.name === 'Content1' ? 'Content2' : 'Content1'
comName.component = defineAsyncComponent(() => import(`./${comName.name}.vue`)) } </script>
|
由于配合defineAsyncComponent
使用,切换时将不会销毁组件,但每次还是会“新”创建,即不会有“销毁”的生命周期。
其他内置组件
官方文档:https://cn.vuejs.org/api/built-in-components.html#built-in-components
Teleport - 传送门
官方文档:https://cn.vuejs.org/guide/built-ins/teleport.html
作用:将其插槽内容渲染到 DOM 中的另一个位置。
1 2 3
| <teleport to="body"> <h1>我将显示在 body 元素内</h1> </teleport>
|
Transition - 过渡态
官方文档:https://cn.vuejs.org/guide/built-ins/transition.html
作用:制作基于状态变化的过渡和动画
1 2 3 4
| <button @click="show = !show">Toggle</button> <Transition> <p v-if="show">hello</p> </Transition>
|
1 2 3 4 5 6 7 8 9
| .v-enter-active, .v-leave-active { transition: opacity 0.5s ease; }
.v-enter-from, .v-leave-to { opacity: 0; }
|
Suspense - 悬停
官方文档:https://cn.vuejs.org/guide/built-ins/suspense.html
作用:用来在组件树中协调对异步依赖的处理
插件
基本使用
1 2 3 4 5 6 7 8 9 10 11
| export default { install(app, options) { console.log(app, options)
} }
app.use(customPlugin, { xx: 1 })
|
场景使用
- 注册全局的组件
- 注册全局的方法,通过挂载到 Vue 的原型链上实现
- 注册全局的自定义指令
1 2 3 4 5 6 7 8 9 10 11 12
| export default { install(app, options) { console.log(app, options)
} }
|
面试题
手写异步组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| import { type Component, ref } from 'vue'
type AsyncComponentLoader = () => Promise<Component>
interface AsyncComponentOptions { loader: AsyncComponentLoader loadingComponent?: Component errorComponent?: Component delay?: number timeout?: number suspensible?: boolean onError?: (error: Error, retry: () => void, fail: () => void, attempts: number) => any }
export function defineAsyncComponent( source: AsyncComponentLoader | AsyncComponentOptions ): Component {
if (typeof source === 'function') { source = { loader: source } }
if (!source.loader) { throw new Error('Loader is required for async component') }
const { loader, delay = 0, timeout = 0, loadingComponent, errorComponent } = source let InnerComp: Component
return { name: 'AsyncComponentWrapper', setup() { const loaded = ref(false) const loading = ref(false) const error = ref<Error | null>(null)
const loaderFn = async () => { loading.value = true try { InnerComp = await loader() loaded.value = true } catch (e) { error.value = e instanceof Error ? e : new Error('Unknown error') } finally { loading.value = false }
let timer: number | undefined if (timeout) { timer = setTimeout(() => { if (!loaded.value) { error.value = new Error('timeout') } else { clearTimeout(timer) } }, timeout) } }
if (delay) setTimeout(loaderFn, delay) else loaderFn()
return () => { if (loaded.value) { return { type: InnerComp } } else if (error.value) { return errorComponent || '错误' } else { return loadingComponent || '加载中' } } } } }
|