Vue3新特性体验--下(各式各样API,完结篇)
这是Vue3新特性体验的最后一篇,推荐先把上、中篇看完,然后继续本文的阅读。
Vue3新特性体验--上(内附简单实例,可直接使用)_前端菜小白leo的博客-CSDN博客
Vue3新特性体验--中(Composition API)_前端菜小白leo的博客-CSDN博客
依然使用与上、中篇同样的简单实例。回顾一下实例目录与页面展示
接下来,继续Vue3新特性的体验之旅。
provide和inject
我们已经了解父子组件通信可以使用props跟emit,那么跨层级(祖孙)通信该如何?可以使用provide和inject可以实现跨层级组件通信。
添加一个Child.vue,其为B.vue的子组件,而B.vue为HelloWorld.vue的子组件,即Child.vue为HelloWorld.vue的孙组件。组件关系代码如下(只展示模板片段)
//HelloWorld.vue
<template>
<div class="hello">
<h1>我是vue3项目</h1>
<div class="content">
<A/>
<B/>
</div>
<div>
<p>我是HelloWorld组件</p>
</div>
</div>
</template>
//B.vue
<template>
<div class="b-style">
<p>我是B组件</p>
<Child />
</div>
</template>
//Child.vue
<template>
<div class="child-style">
<p>我是Child组件</p>
</div>
</template>
组件关系展示页面(只关注B组件部分)
组件搭建好,开始演示使用provide和inject。
HelloWorld.vue
<template>
<div class="hello">
<h1>我是vue3项目</h1>
<div class="content">
<A/>
<B/>
</div>
<div>
<p>我是HelloWorld组件</p>
</div>
</div>
</template>
<script>
import { provide } from '@vue/runtime-core'
import A from './A.vue'
import B from './B.vue'
export default {
components:{
A,
B
},
name: 'HelloWorld',
setup () {
provide('fromHW','HelloWorld的消息') //使用provide向下传递
return {
}
}
}
</script>
Child.vue
<template>
<div class="child-style">
<p>我是Child组件</p>
<span>{{msg}}</span>
</div>
</template>
<script>
import { inject } from '@vue/runtime-core'
export default {
name: 'Child',
setup(){
let msg = inject('fromHW') //通过inject拿到provide中传递的消息
return {
msg
}
}
}
</script>
页面效果,正常展示
注意:inject获取到对应的provide,则第一个字段值必须一致,如该例中的“fromHW”。
这时候Child已经拿到HelloWorld中传递下来的消息,那么反过来呢,Child向HelloWorld传递消息在Vue2中,一种常用方式是事件总线Bus实现跨组件通信
Vue组件间的通信方式(多种场景,通俗易懂,建议收藏)_前端菜小白leo的博客-CSDN博客
但是在Vue3中移除了$on、$off、$once,导致无法使用Bus,但是官方推荐的另外一种取代方案,原理跟Bus一样,那就是mitt.js。
mitt.js
安装mitt
npm i mitt -s
新建mitt.js
import mitt from 'mitt'
const emitter = mitt();
export default emitter;
开始使用mitt.js
Child.vue
<template>
<div class="child-style">
<p>我是Child组件</p>
<span>{{msg}}</span>
<button @click="changeMsg">发送消息</button>
</div>
</template>
<script>
import { inject } from 'vue';
import emitter from '../mitt'
export default {
name: 'Child',
setup(){
let msg = inject('fromHW')
let changeMsg = () => { //执行该方法,触发mitt的emit
emitter.emit('fromChild','Child组件的消息')
}
return {
msg,
changeMsg
}
}
}
</script>
HelloWorld.vue
<template>
<div class="hello">
<h1>我是vue3项目</h1>
<div class="content">
<A/>
<B/>
</div>
<div>
<p>我是HelloWorld组件</p>
<span>{{msg}}</span>
</div>
</div>
</template>
<script>
import { ref,provide } from '@vue/runtime-core';
import emitter from '../mitt'
import A from './A.vue'
import B from './B.vue'
export default {
components:{
A,
B
},
name: 'HelloWorld',
setup () {
let msg = ref('')
provide('fromHW','HelloWorld的消息') //向下传递
emitter.on('fromChild',val => { //监听mitt中的emit
msg.value = val
})
return {
msg
}
}
}
</script>
点击按钮,实现Child向HelloWorld发消息
注意:与provide和inject注意点一致,触发和监听的字段需一致,才能达到指定的通信效果,如该例中的“fromChild”。
既然跨层级组件之间能使用这两种方式,父子组件当然也可以使用。
Teleport
提供一种方法,可以让一个模板插入到该组价或者父组件外的html标签(很可能是body)里面,可以称它为穿梭门。
未使用teleport时:
<template>
<div class="child-style">
<p>我是Child组件</p>
<span>{{msg}}</span>
<button @click="changeMsg">发送消息</button>
<div class="test">
<span>Child未使用teleport</span>
</div>
</div>
</template>
看下页面元素结构
试着使用teleport,穿梭到body
<template>
<div class="child-style">
<p>我是Child组件</p>
<span>{{msg}}</span>
<button @click="changeMsg">发送消息</button>
<!-- <div class="test">
<span>Child未使用teleport</span>
</div> -->
<teleport to="body">
<div class="test">
<span>Child使用teleport</span>
</div>
</teleport>
</div>
</template>
Suspense
官网对其解释
我们有时候想在加载异步组件时渲染一些后备内容(比如loading等),就可以使用suspense,让我们可以优化用户体验。
首先,对Child.vue进行修改,模拟异步加载组件
<template>
<div class="child-style">
<p>我是Child组件</p>
<span>{{msg}}</span>
</div>
</template>
<script>
export default {
name: 'Child',
setup(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve({
msg:'异步加载消息'
})
},2000)
})
}
}
</script>
然后修改父组件B.vue,使用Suspense,其有两个插槽,它们都只接收一个直接子节点,default插槽里的节点会尽可能展示出来。如果不能,则展示fallback插槽里的节点。
<template>
<div class="b-style">
<p>我是B组件</p>
<suspense>
<!-- v-slot:default可以简写成#default -->
<template v-slot:default>
<AsyncChild />
</template>
<!-- v-slot:fallback可以简写成#fallback -->
<template v-slot:fallback>
<div>
<span style="color:#666666">正在加载,请稍后...</span>
</div>
</template>
</suspense>
</div>
</template>
<script>
import { defineAsyncComponent } from "vue"; //vue3中使用其引入异步组件
const AsyncChild = defineAsyncComponent(() => import("./Child.vue"));
export default {
name: "B",
components: {
AsyncChild,
},
setup() {
return {};
},
};
</script>
B组件在加载时,因为Child为异步组件,因此这个时候suspense插槽default无法展示,此时先展示fallback里面的内容。
模拟两秒后(setTimeout) ,Child组件加载完毕,则suspense显示插槽default中的内容。
重要的是,异步组件不需要作为suspense的直接子节点。它可以出现在组件树任意深度的位置,且不需要出现在和suspense自身相同的模板中。只有所有的后代组件都准备就绪,该内容才会被认为解析完毕。如下,当Child加载完毕,“显示完成”才一起出现。
<template>
<div class="b-style">
<p>我是B组件</p>
<suspense>
<!-- v-slot:default可以简写成#default -->
<template v-slot:default>
<div>
<span>显示完成</span>
<AsyncChild />
</div>
</template>
<!-- v-slot:fallback可以简写成#fallback -->
<template v-slot:fallback>
<div>
<span style="color:#666666">正在加载,请稍后...</span>
</div>
</template>
</suspense>
</div>
</template>
将 <suspense>
跟 <transition> 和 <keep-alive> 组件相结合是常见的情形。这些组件的嵌套顺序对于它们的正确工作很重要。更多详细内容请参考官方文档:
Suspense | Vue.jshttps://v3.cn.vuejs.org/guide/migration/suspense.htmlv-model 与 v-bind.sync
语法糖script setup
尽管现在setup使用起来比较方便,但是可以发现一些地方,组件都已经引入了还需要注册,而且当一个组件的数据和方法过多时,需要return很多行,代码量可能会很多。Vue3提供一个语法糖script setup,即在script加入setup,可以不用return,template也可以获取到。
将HelloWorld.vue改成script setup的形式
修改前:
<script>
import { ref,provide } from 'vue';
import emitter from '../mitt'
import A from './A.vue'
import B from './B.vue'
export default {
components:{
A,
B
},
name: 'HelloWorld',
setup () {
let msg = ref('')
provide('fromHW','HelloWorld的消息') //向下传递
emitter.on('fromChild',val => { //监听事件
msg.value = val
})
return {
msg
}
}
}
</script>
修改后代码量少了许多:
<template>
<div id="hello">
<h1>我是vue3项目</h1>
<div class="content">
<A/>
<B/>
</div>
<div>
<p>我是HelloWorld组件</p>
<span>{{msg}}</span>
</div>
</div>
</template>
<script setup>
import { ref,provide } from 'vue';
import emitter from '../mitt'
import A from './A.vue'
import B from './B.vue'
let msg = ref('')
provide('fromHW','HelloWorld的消息') //向下传递
emitter.on('fromChild',val => { //监听事件
msg.value = val
})
</script>
验证页面显示正常,无警告,无报错。
但是没有了setup函数,那么props,emit,attrs该如何获取呢,就要介绍一下新的语法了。setup script语法糖提供了三个新的API来供我们使用:defineProps、defineEmits和useContext。
defineProps:用来接收父组件传来的值props。
defineEmits:用来声明触发的事件表。
useContext:用来获取组件上下文context。
HelloWorld.vue
<template>
<div id="hello">
<h1>我是vue3项目</h1>
<div class="content">
<A/>
<B msg="props传递的消息" @changeB="handleClick"/>
</div>
<div>
<p>我是HelloWorld组件</p>
<span>{{msg}}</span>
</div>
</div>
</template>
<script setup>
import { ref,provide } from 'vue';
import emitter from '../mitt'
import A from './A.vue'
import B from './B.vue'
let msg = ref('')
provide('fromHW','HelloWorld的消息') //向下传递
emitter.on('fromChild',val => { //监听事件
msg.value = val
})
let handleClick = val => { //emit回调
alert(val)
}
</script>
B.vue
<template>
<div class="b-style">
<p>我是B组件</p>
<suspense>
<!-- v-slot:default可以简写成#default -->
<template v-slot:default>
<div>
<span>显示完成</span>
<p>{{props.msg}}</p>
<button @click="changeB">emit</button>
<AsyncChild />
</div>
</template>
<!-- v-slot:fallback可以简写成#fallback -->
<template v-slot:fallback>
<div>
<span style="color:#666666">正在加载,请稍后...</span>
</div>
</template>
</suspense>
</div>
</template>
<script setup>
import { defineAsyncComponent, defineProps, defineEmits} from "vue";
let AsyncChild = defineAsyncComponent(() => import("./Child.vue"));
let emit = defineEmits(['changeB'])
let props = defineProps({ //接收父组件放在子组件的msg
msg: String,
})
let changeB = () => { //触发父组件放在子组件的事件changeB
emit('changeB', 'emit传递的消息')
}
</script>
刷新页面显示,defineProps使用成功。
点击按钮,alert弹窗,defineEmits使用成功。
对比未使用script setup
props: ['msg'],
setup(props, {attrs, slots, emit}) {
const changeB = () => {
emit('changeB','emit传递的消息');
};
return {
changeB,
};
}
总结:通过简单实例体验Vue3一些常用的新特性,感受Vue3带来的便利性。
Vue3内容远远不止于此,更多Vue3详细内容请前往官方地址学习
介绍 | Vue.jsVue.js - The 渐进式 JavaScript 框架https://v3.cn.vuejs.org/guide/migration/introduction.html
相关文章
- Vue3.x语法糖script setup和相关API的使用
- vue3组合式api 和 react自定义hooks
- 在 Vue3 成为默认版本后,盘点了 Vue3 与 Vue2 的区别
- 何为 Vue3 组件标注 TS 类型,看这篇文章就够了!
- Vue3+elementplus搭建通用管理系统实例十二:使用通用表格、表单实现对应功能
- vue3之vue2到vue3到底变化了什么
- 被迫开始学习Typescript —— vue3的 props 与 interface
- vue3响应式reactive的实现原理;proxy深层代理;vue3响应式api
- vue3组件内部改变props的值
- Vue3实践指南:使用reactive函数声明数组如何正确赋值响应式、script setup语法糖中toRefs如何优雅呈现、Options API 与 Composition API 如何选择及混用是否对性能有影响、关于 setup 中没有 this 的问题及 setup 的执行时机
- 浅析Vue3新特性-Teleport(任意传送门也称瞬间移动):为什么需要Teleport、与React的Portals特性、如何使用(直接使用、与组件搭配使用、使用多个teleport)、teleport API(to及disabled使用介绍)
- vue3组合API注意点(系列三)
- vue3创建项目(系列一)
- 24 vue3使用vuex--- actions的使用