目录
一、Fragment
- 在Vue2中:组件必须有一个根标签
- 在Vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 好处:减少标签层级,减小内存占用
二、Teleport
Teleport:是一中能够将组件html结构移动到指定位置的技术
2.1 案例练习
- App.vue作为父组件,Child.vue作为子组件,DeepChild作为深层子组件,Popup.vue作为DeepChild组件内的弹窗组件
2.1.1 未使用Teleport组件
App.vue
<template> <div class="app"> <h3>App组件 - 父组件</h3> <Child></Child> </div> </template> <script> import Child from "./components/Child.vue"; export default { name: "App", components: { Child }, }; </script> <style> .app { background-color: gray; padding: 10px; } </style>
Child.vue
<template> <div class="child"> <h3>Child组件 - 子组件</h3> <DeepChild></DeepChild> </div> </template> <script> import DeepChild from "./DeepChild.vue"; export default { name: "Child", components: { DeepChild }, }; </script> <style> .child { background-color: skyblue; padding: 10px; } </style>
DeepChild.vue
<template> <div class="DeepChild"> <h3>DeepChild组件 - 深层子组件</h3> <Popup></Popup> </div> </template> <script> import Popup from "./Popup.vue"; export default { name: "DeepChild", components: { Popup }, }; </script> <style> .DeepChild { background-color: orange; padding: 10px; } </style>
Popup.vue
<template> <div> <button @click="isShow = true">点击弹窗</button> <div v-if="isShow" class="Popup"> <h3>弹窗组件</h3> <button @click="isShow = false">关闭弹窗</button> </div> </div> </template> <script> import { ref } from "vue"; export default { name: "Popup", setup() { let isShow = ref(false); return { isShow }; }, }; </script> <style> .Popup { width: 300px; height: 300px; text-align: center; line-height: 100px; background-color: pink; } </style>
由图示可看出,未使用 Teleport组件,Popup组件 是直接显示在 DeepChild组件内部,且会使整体框架扩张
2.1.2 使用Teleport组件
在Popup.vue内使用 Teleport组件
// ### Popup.vue
<template>
<div>
<button @click="isShow = true">点击弹窗</button>
<!-- 使用teleport组件 -->
<teleport to="body">
<div v-if="isShow" class="Popup">
<h3>弹窗组件</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</teleport>
</div>
</template>
....
由图示可看出Pop组件经 Teleport组件 被移动到了 body标签之下
2.1.3 添加遮罩效果
- 为弹窗添加一个遮罩效果
Popup.vue
<template>
<div>
<button @click="isShow = true">点击弹窗</button>
<!-- 使用teleport组件 -->
<teleport to="body">
<div v-if="isShow" class="mask">
<div v-if="isShow" class="Popup">
<h3>弹窗组件</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import { ref } from "vue";
export default {
name: "Popup",
setup() {
let isShow = ref(false);
return { isShow };
},
};
</script>
<style>
.mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
}
.Popup {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
height: 300px;
text-align: center;
line-height: 100px;
background-color: pink;
}
</style>
三、Suspense组件
3.1 简介
等待异步组件时渲染一些额外的内容,让应用有更好的用户体验
使用步骤:
(1)异步引入组件
import { defineAsyncComponent } from "vue";
const Child = defineAsyncComponent(() => import("./components/Child.vue"));
(2)使用 Suspense 包裹组件,并配置好 default 与 fallback
<template>
<div class="app">
<h3>App组件 - 父组件</h3>
<Suspense>
<template v-slot:default>
<Child></Child>
</template>
<template v-slot:fallback>
<h3>加载中...</h3>
</template>
</Suspense>
</div>
</template>
3.2 案例练习
- App.vue 父组件用 Suspense 包裹 Child.vue 异步子组件,并配置好 default 与 fallback
- Child.vue内设置定时器,延迟数据展示
- 在异步子组件未加载完成时,子组件位置显示:加载中
Child.vue
<template> <div class="child"> <h3>Child组件 - 子组件</h3> {{ sum }} </div> </template> <script> import { ref } from "vue"; export default { name: "Child", async setup() { let sum = ref("用于定时器数据"); let p = new Promise((resolve, reject) => { setTimeout(() => { resolve({ sum }); }, 1000); }); return await p; }, }; </script> <style> .child { background-color: skyblue; padding: 10px; } </style>
App.vue
<template> <div class="app"> <h3>App组件 - 父组件</h3> <Suspense> <template v-slot:default> <Child></Child> </template> <template v-slot:fallback> <h3>加载中...</h3> </template> </Suspense> </div> </template> <script> // import Child from "./components/Child.vue"; //静态引入 import { defineAsyncComponent } from "vue"; const Child = defineAsyncComponent(() => import("./components/Child.vue")); // 异步引入 export default { name: "App", components: { Child }, }; </script> <style> .app { background-color: gray; padding: 10px; } </style>
四、Vue3中其他较为重要的改变
4.1 全局API转移
- Vue2.x有许多全局API 和 配置
- 例如:注册全局组件、注册全局指令等
// ###注册全局组件
Vue.component('button-counter', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
// ###注册全局指令
Vue.directive('focus', {
inserted: el => el.focus()
})
- Vue3.0中对这些API做出了调整
- 将全局API,即 Vue.xxx 调整到了应用实例(app)上
2.x 全局 API | 3.x 实例 API (app) |
---|---|
Vue.config | app.config |
Vue.config.productionTip | 移除 (见官方文档) |
Vue.config.ignoredElements | app.config.compilerOptions.isCustomElement (见官方文档) |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use (见官方文档) |
Vue.prototype | app.config.globalProperties (见官方文档) |
Vue.extend | 移除 (见官方文档) |
4.2 其他改变
(1)data选项应始终被声明为一个函数
(2)过度类名的更改:
Vu2.x写法
.v-enter,
.v-leave-to{
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
Vue3.x写法
.v-enter-from,
.v-leave-to{
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
(3)移除 keyCode 作为 v-on 的修饰符,同时也不再支持 config.keyCodes
(4)移除 v-on.native 修饰符
v-on 的 .native 修饰符已被移除。同时,新增的 emits 选项允许子组件定义真正会被触发的事件。
Vue3.x 语法
父组件中绑定事件
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
子组件中声明自定义事件
<script>
export default {
emits: ['close']
}
</script>
(5)移除过滤器
在 3.x 中,过滤器已移除,且不再支持。取而代之的是,建议用方法调用或计算属性来替换它们。