🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# Teleport-Suspense-Fragment-defineAsyncComponent ## 1. Teleport Teleport 就像是哆啦 A 梦中的「任意门」,任意门的作用就是可以将人瞬间传送到另一个地方。有了这个认识,我们再来看一下为什么需要用到 Teleport 的特性呢, 看一个小例子: 在子组件`Header`中使用到`Dialog`组件,我们实际开发中经常会在类似的情形下使用到 `Dialog` ,此时`Dialog`就被渲染到一层层子组件内部,处理嵌套组件的定位、`z-index`和样式都变得困难。 `Dialog`从用户感知的层面,应该是一个独立的组件,从 dom 结构应该完全剥离 Vue 顶层组件挂载的 DOM;同时还可以使用到 Vue 组件内的状态(`data`或者`props`)的值。 简单来说就是,**即希望继续在组件内部使用`Dialog`, 又希望渲染的 DOM 结构不嵌套在组件的 DOM 中**。 此时就需要 Teleport 上场,我们可以用`<Teleport>`包裹`Dialog`, 此时就建立了一个传送门,可以将`Dialog`渲染的内容传送到任何指定的地方。 接下来就举个小例子,看看 Teleport 的使用方式 ### 1-2. Teleport 的使用 我们希望 Dialog 渲染的 dom 和顶层组件是兄弟节点关系, 在`index.html`文件中定义一个供挂载的元素: ```html <body> <div id="app"></div> <div id="dialog"></div> </body> ``` 定义一个`Dialog`组件`Dialog.vue`, 留意`to`属性, 与上面的`id`选择器一致: ```html <template> <teleport to="#dialog"> <div class="dialog"> <div class="dialog_wrapper"> <div class="dialog_header" v-if="title"> <slot name="header"> <span>{{ title }}</span> </slot> </div> </div> <div class="dialog_content"> <slot></slot> </div> <div class="dialog_footer"> <slot name="footer"></slot> </div> </div> </teleport> </template> ``` 最后在一个子组件`Header.vue`中使用`Dialog`组件, 这里主要演示 Teleport 的使用,不相关的代码就省略了。`header`组件 ```html <div class="header"> ... <navbar /> <Dialog v-if="dialogVisible"></Dialog> </div> ``` ![](https://img.kancloud.cn/b6/4c/b64ca90c7d67da70642e8c8b5b9ec067_1006x475.png) 我们使用`teleport`组件,通过`to`属性,指定该组件渲染的位置与`<div id="app"></div>`同级,也就是在`body`下,但是`Dialog`的状态`dialogVisible`又是完全由内部 Vue 组件控制. ## 2. Suspense `Suspense`是 Vue3.x 中新增的特性, 那它有什么用呢?别急,我们通过 Vue2.x 中的一些场景来认识它的作用。 Vue2.x 中应该经常遇到这样的场景: ``` <template> <div> <div v-if="!loading"> ... </div> <div v-if="loading"> 加载中... </div> </div> </template> ``` 在前后端交互获取数据时, 是一个异步过程,一般我们都会提供一个加载中的动画,当数据返回时配合`v-if`来控制数据显示。 如果你使用过`vue-async-manager`这个插件来完成上面的需求, 你对`Suspense`可能不会陌生,Vue3.x 感觉就是参考了`vue-async-manager`. Vue3.x 新出的内置组件`Suspense`, 它提供两个`template` slot, 刚开始会渲染一个 fallback 状态下的内容, 直到到达某个条件后才会渲染 default 状态的正式内容, 通过使用`Suspense`组件进行展示异步渲染就更加的简单。**如果使用 `Suspense`, 要返回一个 promise** ### 2-2. `Suspense` 组件的使用: ``` <Suspense> <template #default> <async-component></async-component> </template> <template #fallback> <div> Loading... </div> </template> </Suspense> ``` `asyncComponent.vue`: ``` <<template> <div> <h4>这个是一个异步加载数据</h4> <p>用户名:{{user.nickname}}</p> <p>年龄:{{user.age}}</p> </div> </template> <script> import { defineComponent } from "vue" import axios from "axios" export default defineComponent({ setup(){ const rawData = await axios.get("http://xxx.xinp.cn/user") return { user: rawData.data } } }) </script> ``` 从上面代码来看,`Suspense`只是一个带插槽的组件,只是它的插槽指定了`default`和`fallback`两种状态 ## Fragment 在vue2里面,有根标签,但是到来vue3,它是自动给你创建个虚拟根标签`VNode`(`Fragment`),所以可以不要根标签。好处就是**减少标签层级, 减小内存占用** ```html <template> <span>hello</span> <span>world</span> </template> ```