Vue3 新特性
-
首先是向下兼容,Vue3 支持大多数 Vue2 的特性。甚至就拿 Vue2 的语法开发 Vue3,也是没有任何问题的。
-
性能的提升,每个人都希望使用的框架更快,更轻。Vue3 做到了,给开发者一个极致的体验。官方网站给出的数据是:打包大小减少 41%,初次渲染快 55%,更新快 133%,内存使用减少 54%。
-
新推出的Composition API ,在 Vue2 中遇到的问题就是复杂组件的代码变的非常麻烦,甚至不可维护。说白了就是封装不好,重用不畅。这个Composition API一推出,立马解决了这个问题。它是一系列 API 的合集。
-
其他新特性:Teleport(瞬移组件)、Suspense(解决异步加载组件问题)和全局 API 的修改和优化。
-
更好TypeScript支持,Vue3 的源代码就是使用TypeScript进行开发的。所以在新的版本上使用TS也更加顺畅无阻。
一、composition-api
Vue在2.x中编写代码要按照一定的模板,比如数据就只能放在data()中,方法只能放在methods中,按照模板编写代码对于新手来讲可能是好事,但是一旦项目变大,维护起来就显得很困难。
下图的左边图示,即Vue2使用的Options-api,图中相同的颜色对应是组件的一种功能,可以看到为了实现一种功能,Options-api所写的代码是非常分散的。
如果组件逻辑复杂,代码量多,我们添加新代码不光要不停的上下滑动,而且在后期代码维护中,阅读起来也变得十分的困难,因为实现一种功能的代码并没有集中在一起。另外就是作为一个新接手的开发人员,在茫茫的 method、data、computed 等选项中一目了然的发现这个变量是属于哪个功能是比较困难的 。
而在Composition-api中,我们可以把实现一种功能的代码写在一起,甚至还可以把它们单独抽取在一个js文件或者一个函数中。在js文件中也可以引用Composition-api的生命周期函数。这将极大的提高代码的可维护性。这样就可以更好的提取和重用多个组件之间的逻辑。

优劣比较:
-
在逻辑组织和逻辑复用方面,Composition API是优于Options API,因为Composition API几乎是函数,会有更好的类型推断。
-
Composition API对 tree-shaking 友好,代码也更容易压缩。
-
Composition API中见不到this的使用,减少了this指向不明的情况。
-
如果是小型组件,可以继续使用Options API,也是十分友好的。
1、setup
setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。 新的 setup 选项在组件被创建之前执行,一旦 props 被解析完成,它就将被作为组合式 API 的入口。
setup函数的参数
我们先来研究一个setup函数的参数,它主要有两个参数:
- 第一个参数:props
- 第二个参数:context
props非常好理解,它其实就是父组件传递过来的属性会被放到props对象中,我们在setup中如果需要使用,那么就可以直接通过props参数获取:
- 对于定义props的类型,我们还是和之前的规则是一样的,在props选项中定义;
- 并且在template中依然是可以正常去使用props中的属性,比如message;
- 如果我们在setup函数中想要使用props,那么不可以通过 this 去获取(后面我会讲到为什么);
- 因为props有直接作为参数传递到setup函数中,所以我们可以直接通过参数来使用即可;
另外一个参数是context,我们也称之为是一个SetupContext,它里面包含三个属性:
- attrs:所有的非prop的attribute;
- slots:父组件传递过来的插槽(这个在以渲染函数返回时会有作用);
- emit:当我们组件内部需要发出事件时会用到emit(因为我们不能访问this,所以不可以通过 this.$emit发出事件);
setup函数的返回值
setup既然是一个函数,那么它也可以有返回值,它的返回值用来做什么呢?
- setup的返回值可以在模板template中被使用;
- 也就是说我们可以通过setup的返回值来替代data选项;
甚至是我们可以返回一个执行函数来代替在methods中定义的方法:
<template>
<div>
<div>{{ title }}</div>
<div @click="hanldeClick">点他</div>
</div>
</template>
<script>
export default {
setup() {
const title = 'this is a title'
const hanldeClick = () => {
alert('已点!')
}
return {
title,
hanldeClick
}
},
}
</script>
setup不可以使用this
官方关于this有这样一段描述:
-
表达的含义是this并没有指向当前组件实例;
-
并且在setup被调用之前,data、computed、methods等都没有被解析;
-
所以无法在setup中获取this;
2、Ref 和 Reactive
在我看来 ref 和 reactice 都是用来创建响应式对象的。
- reactive 接受的参数是一个对象或数组类型。如果是数组类型会转换成proxy对象。
- ref 一般创建一个基本类型变量,有一个 .value属性,可以通过其对值进行读取或修改。
- reactive 在组合函数返回时记得添加上 ’...toRefs(state)‘以保持响应性,对对象解构或展开后会失去响应性,所以需要使用roRefs()把reactive类型转为ref类型。
- ref 可以用于子组件的ref属性使用。(后面会提到)
<template>
<div>
<h2>{{ title }}</h2>
<h2>{{ rTitle }}</h2>
<div @click="hanldeClick">点他</div>
</div>
</template>
<script>
import {reactive, ref, toRefs} from "vue";
export default {
setup() {
const title = ref('this is a title')
const isTrye = ref(true)
const state = reactive({
rTitle: 'this is reactive title'
})
const hanldeClick = () => {
state.rTitle = '111'
title.value = '222'
isTrye.value = false
}
return {
...toRefs(state)
title,
isTrye,
hanldeClick
}
},
}
</script>
以下是两种关于Ref ,Reactive的风格建议
- 就像你在普通 JavaScript 中区别声明基础类型变量与对象变量时一样区别使用 ref 和 reactive。我们推荐你在此风格下结合 IDE 使用类型系统。
- 所有的地方都用 reactive,然后记得在组合函数返回响应式对象时使用 toRefs。这降低了一些关于 ref 的心智负担,但并不意味着你不需要熟悉这个概念。
3、computed 、watch、watchEffect
<script>
import {reactive, ref, toRefs, computed, watch} from "vue";
export default {
setup() {
// computed
const count = ref(1)
const plusOne = computed(() => count.value + 1)
const state = reactive({
title: computed(() => {
return count.value + 1
}),
})
// watch
// watch 可以接收三个参数 第一个是要监听的对象 第二个是数据处理变化,第三个是配置项(options)
// options: immediate:true是刚一进去就监听一次
// 1:监听ref定义的响应式数据
const num = ref(0)
const msg = ref('watch')
watch(
num,
(newV, oldV) => {
console.log(newV)
},
{ immediate:true }
)
// 2:监听多个ref声明的响应式数据
watch(
[num, msg],
(newV, oldV) => {
console.log(newV)
},
{ immediate:true }
)
// 3:监听reactive定义的响应式的全部数据
// 注意:使用reactive定义的数据,无法正确获取odlV;并且强制开启了深度检测(deep配置无效)
const state = reactive({
age: 18,
name: 'fc'
})
watch(
state,
(newV, oldV) => {
console.log(newV)
},
{ deep: true } // 此配置无效
)
// 4:监听reactive所定义的一个响应式数据的一个属性
watch(
() => state.age,
(newV, oldV) => {
console.log(newV)
}
)
// 5:监听reactive所定义的一个响应式数据的一些属性
watch(
[() => state.age, () => state.name],
(newV, oldV) => {
console.log(newV)
}
)
// 6:当监听的声明对象内的属性还是一个对象,因为不是使用reactive直接声明的
const person = {
job: {
j1: 1
}
}
watch(
() => peison.job,
(newV, oldV) => {
console.log(newV)
},
{ deep: true }//此处由于监视的是reactive定义的对象内的某个属性,所以deep配置有效
)
return {
...toRefs(state)
}
},
}
</script>
watchEffect
- watch是:既要指明监视的属性,也要指明监视的回调。
- watchEffect是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
这个函数的功能和计算属性差不多,但是
- computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
<script>
import {watchEffect} from "vue";
export default {
setup() {
watchEffect(() => {
if (state.value) {
// do something ...
setDates(state.value)
}
})
}
}
4、生命周期
- setup() :开始创建组件之前,在beforeCreate和created之前执行。创建的是data和method
- onBeforeMount() : 组件挂载到节点上之前执行的函数。
- onMounted() : 组件挂载完成后执行的函数。
- onBeforeUpdate(): 组件更新之前执行的函数。
- onUpdated(): 组件更新完成之后执行的函数。
- onBeforeUnmount(): 组件卸载之前执行的函数。
- onUnmounted(): 组件卸载完成后执行的函数
- onActivated(): 被包含在中的组件,会多出两个生命周期钩子函数。被激活时执行。
- onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行。
- onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。
- onRenderTracked(): 直译过来就是状态跟踪,它会跟踪页面上所有响应式变量和方法的状态,也就是我们用return返回去的值,他都会跟踪。只要页面有update的情况,他就会跟踪,然后生成一个event对象,我们通过event对象来查找程序的问题所在。
- onRenderTriggered(): 直译是状态触发,它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。
5、模板指令
- 组件上 v-model 用法已更改,支持双向绑定多个属性,例如v-model.title="title"
- key 属性
- Vue 3.x 不建议在 v-if/v-else/v-else-if 的分支中使用 key,如果非要使用,请设置唯一的key值。
- Vue 3.x 可以将 key值 设置在template 上 (Vue2.x 需要将key值设置到子节点上)
- v-if 与 v-for 的优先级对比
- 2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用。Vue2.x v-for 优先级高
- 3.x 版本中 v-if 总是优先于 v-for 生效。Vue3.x v-if 优先级高
- v-bind 合并行为
- 在 2.x,如果一个元素同时定义了 v-bind=“object” 和一个相同的单独的 property,那么这个单独的 property 总是会覆盖 object 中的绑定。 单独的属性覆盖v-bind
- 在 3.x,如果一个元素同时定义了 v-bind=“object” 和一个相同的单独的 property,那么声明绑定的顺序决定了它们如何合并。
<!-- vue 2.x -->
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div
<!-- result -->
<div id="red"></div>
<!-- vue 3.x -->
<!-- template -->
<div id="red" v-bind="{ id: 'blue' }"></div
<!-- result -->
<div id="blue"></div>
<!-- template -->
<div v-bind="{ id: 'blue' }" id="red" ></div
<!-- result -->
<div id="red"></div>
- v-for 中的 Ref 数组
- 在 Vue 2 中,在 v-for 里使用的 ref attribute 会用 ref 数组填充相应的 $refs property。当存在嵌套的 v-for 时,这种行为会变得不明确且效率低下。
- 在 Vue 3 中,这样的用法将不再在 $ref 中自动创建数组。要从单个绑定获取多个 ref,请将 ref 绑定到一个更灵活的函数上 (这是一个新特性)
<div v-for="item in list" :key="item" :ref="setItemRef"></div>
export default {
setup() {
let itemRefs = []
const setItemRef = el => {
if (el) {
itemRefs.push(el)
}
}
return {
setItemRef
}
},
}
</script>
- itemRefs 不必是数组:它也可以是一个对象,其 ref 会通过迭代的 key 被设置。
- 如果需要,itemRef 也可以是响应式的且可以被监听。
6、props emit ref
直接上代码吧 ~
<!-- 父组件 -->
<template>
<div>
<h2>{{ title }}</h2>
<child :toChildTitle="toChildTitle" @handleChange="handleChange" ref="childRef"></child>
</div>
</template>
<script>
import {onMounted, reactive, toRefs, ref} from "vue";
export default {
setup() {
// 需要定义 ref 注意需要return出去
const childRef = ref()
const state = reactive({
title: '原来的title',
toChildTitle: 'fa传递过去title'
})
onMounted(() => {
// 父组件 获取/修改 子组件数据
console.log(childRef.value.val)
// 父组件 触发 子组件方法
childRef.value.childFun()
})
const handleChange = (val) => {
state.title = handleChange
}
return {
...toRefs(state),
childRef,
handleChange
}
},
}
</script>
<!-- 子组件 -->
<template>
<div>
<h2>{{ toChildTitle }}</h2>
<div @click="changeBtn">点它</div>
</div>
</template>
<script>
import {reactive, ref, toRefs, watch} from "vue";
export default {
props: {
toChildTitle: String,
},
setup(props, context) {
const state = reactive({
toChildTitle: props.toChildTitle, // 将prop传递来的值转换成响应式数据,这样修改这个toChildTitle的时候 父组件的toChildTitle值也会变化
val: '其他参数', // 父组件需要使用的数据 需要return出去
})
// 触发父组件的方法
const changeBtn = () => {
context.emit('handleChange', '从child修改后的title')
}
// 父组件需要使用的方法 也需要return出去
const childFun = () => {
alert('child 的方法')
}
return {
...toRefs(state),
changeBtn,
childFun
}
},
}
</script>
7、provide 和 inject
provide 和inject 是vue提供的一对API 这对API 可以实现组件之间的通信 无论层级有多深 都可以通过这对API 来实现
provide
在 setup() 中使用 provide 时,我们首先从 vue 显式导入 provide 方法。这使我们能够调用 provide 来定义每个 property。 provide 函数允许你通过两个参数定义 property:
- name ( 类型)
- value
<script>
import { provide } from 'vue'
export default {
setup() {
provide('location', 'North Pole')
provide('geolocation', {
longitude: 90,
latitude: 135
})
}
}
</script>
inject
在 setup() 中使用 inject 时,也需要从 vue 显式导入。导入以后,我们就可以调用它来定义暴露给我们的组件方式。 inject 函数有两个参数:
- 要 inject 的 property 的 name
- 默认值 (可选)
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
return {
userLocation,
userGeolocation
}
}
}
</script>
响应式
- 添加响应性: 为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 或 reactive。
<script>
import { provide, reactive, ref } from 'vue'
export default {
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
provide('location', location)
provide('geolocation', geolocation)
}
}
</script>
现在,如果这两个 property 中有任何更改,子组件中的值也将自动更新!
- 修改响应式 property:
当使用响应式 provide / inject 值时,官方中
建议尽可能将对响应式 property 的所有修改限制在定义 provide 的组件内部。
// 父组件
export default {
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
// 修改provide中值的方法
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation)
}
}
</script>
// 子组件
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
// 子组件调用父组件中修改值的方法
const updateUserLocation = inject('updateLocation')
return {
userLocation,
userGeolocation,
updateUserLocation
}
}
}
</script>
如果要确保通过 provide 传递的数据不会被 inject 的组件更改,官方建议对提供者的 property 使用 readonly。
import { provide, reactive, readonly, ref } from 'vue'
export default {
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', readonly(location))
provide('geolocation', readonly(geolocation))
provide('updateLocation', updateLocation)
}
}
</script>
8、
单文件组件 Composition API 语法糖 (
-
组件引入了还要注册
-
属性和方法都要在setup函数中返回,有的时候仅一个return就十几行甚至几十行
-
不想写啊怎么办
好办,Vue3官方提供了script setup语法糖
<script setup>
其实是使用组合式 API 的编译时语法糖。相比于普通的
-
更少的样板内容,更简洁的代码。
-
能够使用纯 Typescript 声明 props 和抛出事件。
-
更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
-
更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)
只需要在script标签中添加setup,组件只需引入不用注册,属性和方法也不用返回,setup函数也不需要,甚至export default都不用写了,不仅是数据,计算属性和方法,甚至是自定义指令也可以在我们的template中自动获得。
但是这么过瘾的语法糖,还是稍微添加了一点点心智负担,因为没有了setup函数,那么props,emit,attrs怎么获取呢,就要介绍一下新的语法了。
setup script 语法糖提供了几个新的API
来供我们使用:defineProps
、defineEmits
-
defineProps 用来接收父组件传来的值props。
-
defineEmits 用来声明触发的事件表。
<template>
<div>
<h1 @click="handleClick">{{ data }}</h1>
<span>{{ count }}</span>
<son></son>
</div>
</template>
<script setup>
import { ref, defineProps, defineEmits } from 'vue'
// 属性和方法不需要返回,直接用
const data = 'this is data'
const count = ref(0)
function handleClick() {
console.log('点击...')
}
// 组件不需要components注册
import son form './son.vue'
// 组件数据传递 props 和 emit 语法改变 => defineProps 、defineEmits
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// 对外暴露属性 - defineExpose
const childText = ref('子组件数据')
defineExpose({
childText
})
// 不必再配合 async 就可以直接使用 await 了,这种情况下,组件的 setup 会自动变成 async setup
const post = await fetch('/api').then(() => {})
// nextTick
import { nextTick } from 'vue'
nextTick(() => {
// ,,,
})
// css 变量注入
const state = reactive({
color: 'red'
})
</script>
<style scoped>
span {
// 使用 v-bind 绑定state中的变量
color: v-bind('state.color')
}
</style>
9、style 特性
scoped
当 标签带有 scoped,它的 CSS 只会应用到当前组件的元素上。
深度选择器:处于 scoped 样式中的选择器如果想要做更“深度”的选择,也即:影响到子组件,可以使用 :deep() 这个伪类:
<style scoped>
.a :deep(.b) {
/* ... */
}
</style>
TIP
通过 v-html 创建的 DOM 内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。
module
<style module>
标签会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件:
<template>
<p :class="$style.red">
This should be red
</p>
</template>
<style module>
.red {
color: red;
}
</style>
自定义注入名称:
<template>
<p :class="classes.red">red</p>
</template>
<style module="classes">
.red {
color: red;
}
</style>
与组合式 API 一同使用: 注入的类可以通过 useCssModule API 在 setup() 和
// 默认,返回 <styel module> 中的类
useCssModule()
// 命名,返回 <style module="classes"> 中的类
useCssModule('classes')
v-bind
单文件组件的 标签可以通过 v-bind 这一 CSS 函数将 CSS 的值关联到动态的组件状态上:
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style>
.text {
color: v-bind(color);
}
</style>
这个语法同样也适用于
<template>
<p>hello</p>
</template>
<script setup>
const theme = {
color: 'red'
}
</script>
<style scoped>
p {
color: v-bind('theme.color');
}
</style>
单文件组件状态驱动的 CSS 变量 () (非官方文档中看到) <template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
10、getCurrentInstance
getCurrentInstance()
是Vue3.x中的核心方法,用于访问实例上下文的router及vuex等。
- 概述:一个很重要的方法,获取当前组件的实例、上下文来操作router和vuex等。
- 引入:由vue提供,按需引入:import { getCurrentInstance} from 'vue';
- 使用:获取当前组件的上下文,推荐使用:const { proxy } = getCurrentInstance()。
import { getCurrentInstance } from 'vue';
// 获取当前组件实例
const instance = getCurrentInstance();
// 获取当前组件的上下文,下面两种方式都能获取到组件的上下文。
const { ctx } = getCurrentInstance(); // 方式一,这种方式只能在开发环境下使用,生产环境下的ctx将访问不到
const { proxy } = getCurrentInstance(); // 方式二,此方法在开发环境以及生产环境下都能放到组件上下文对象(推荐)
// ctx 中包含了组件中由ref和reactive创建的响应式数据对象,以及以下对象及方法;
proxy.$attrs
proxy.$data
proxy.$el
proxy.$emit
proxy.$forceUpdate
proxy.$nextTick
proxy.$options
proxy.$parent
proxy.$props
proxy.$refs
proxy.$root
proxy.$slots
proxy.$watch
二、vue3周边生态
1、vue-router
-
vue2.x中,可以通过this. route来获取或者操作路由。
-
在vue3.0中,引入了Composition-api。在setup函数中无法使用this获取组件实例。新版本的vue-router也提供了支持Composition-api的hooks,例如useRouter,useRoute函数。
import {onMounted} from 'vue'
import {useRoute, useRouter} from "vue-router";
export default {
setup() {
const router = useRouter()
const route = useRoute()
onMounted(() => {
const {id = ''} = route.params
})
function pushWithQuery(query) {
router.push('/index')
}
},
}
另外,vue-router还提供了支持的Composition-api的两个路由守卫:update and leave
- beforeRouteLeave:离开当前页面路由时触发,return false则阻止跳转,next中不能写参数
- beforeRouteUpdate:动态路由 只有参数发生变化是才执行(通俗理解及跳转页面时)
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
export default {
setup() {
onBeforeRouteLeave((to, from) => {
const answer = window.confirm(
'Do you really want to leave? you have unsaved changes!'
)
if (!answer) return false
})
const userData = ref()
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
userData.value = await fetchUser(to.params.id)
}
})
},
}
2、vuex
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
onMounted(() => {
const data = store.state.someData
})
const handleClick = () => {
store.commit('CHANGE_DATA', { a: 1})
}
},
}
三、其他
1、Teleport
Teleport 在国内大部分都翻译成了瞬间移动组件或任意传送门,也有把这个函数叫独立组件。 是一种能够将我们的模板移动到 DOM
中 Vue app
之外的其他位置的技术。
场景:像 modals
,toast
等这样的元素,很多情况下,我们将它完全的和我们的 Vue 应用的 DOM 完全剥离,管理起来反而会方便容易很多。
原因在于如果我们嵌套在 Vue 的某个组件内部,那么处理嵌套组件的定位、z-index
和样式就会变得很困难。
另外,像 modals
,toast
等这样的元素需要使用到 Vue 组件的状态(data
或者 props
)的值。
这就是 Teleport 派上用场的地方。我们可以在组件的逻辑位置写模板代码,这意味着我们可以使用组件的 data 或 props。然后在 Vue 应用的范围之外渲染它。
使用
Teleport方法,可以把Dialog组件渲染到你任意想渲染的外部Dom上,不必嵌套再#app里了,这样就不会互相干扰了。你可以把Teleport看成一个传送门,把你的组件传送到你需要的地方。 teleport组件和其它组件没有任何其它的差异,用起来都是一样的。
- 首先我们在
index.html
中添加我们需要传送到的位置。
<div id="app"></div>
<div id="modal"></div>
- 将编写的组件包装在 teleport 组件中,还需要指定一个 to 属性,为该属性分配一个查询选择器,以标识目标元素。
<!-- to 属性就是目标位置 -->
<teleport to="#modal">
<div v-if="visible" class="modal-wrap">
<div class="modal-msg">我是一个 modal 文案</div>
</div>
</teleport>
2、Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
试验性
Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。特此声明,以便社区能够为当前的实现提供反馈。
生产环境请勿使用。
以上是官方的警告!
使用:
- 首先我们先写一个异步组件
注意点:如果你要使用Suspense的话,要返回一个promise对象,而不是原来的那种JSON对象。
<template>
<div>{{ result }}</div>
</template>
<script>
import axios from 'axios'
import { defineComponent } from 'vue'
export default {
setup() { //promise 语法糖 返回之后也是promise对象
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve({ result: "this is result" });
}, 2000);
});
}
}
</script>
- 使用suspense组件
<template>
<div>
<Suspense>
// 这两个插槽名称是固定的
// defalut:这里面写的就是我们正常要显示的组件和代码
// fallback:这里写的就是应急代码,就是正常代码没有显示的时候的代码
<template #default>
<AsyncShow />
</template>
<template #fallback>
<h1>Loading...</h1>
</template>
</Suspense>
</div>
</template>
export default {
components: {
AsyncShow: defineAsyncComponent(() => import('./AsyncShow.vue'))
}
}
- 处理异步请求错误
在异步请求中必须要作的一件事情,就是要捕获错误,因为我们没办法后端给我们返回的结果,也有可能服务不通,所以一定要进行捕获异常和进行处理。
在vue3.x的版本中,可以使用onErrorCaptured这个钩子函数来捕获异常。在使用这个钩子函数前,需要先进行引入.
<template>
<div>
<h1 v-if="error">load async component error</h1>
<Suspense>
<template #default>
<AsyncShow />
</template>
<template #fallback>
<h1>Loading...</h1>
</template>
</Suspense>
</div>
</template>
import { ref, defineAsyncComponent, onErrorCaptured} from "vue";
export default {
components: {
AsyncShow: defineAsyncComponent(() => import('./AsyncShow.vue'))
},
setup() {
const error = ref(null);
onErrorCaptured(e => {
error.value = e;
return false;
});
return { error };
}
}
有了onErrorCaptured就可以直接在setup()函数中直接使用了。钩子函数要求我们返回一个布尔值,代表错误是否向上传递。这里的false一方面表示:错误不会冒泡给父组件;另一方面表示vue将停止该错误的传播。
3、片段(Fragment)
在 Vue2.x 中, template中只允许有一个根节点:
<template>
<div>
<span></span>
<span></span>
</div>
</template>
但是在 Vue3.x 中,你可以直接写多个根节点:
<template>
<span></span>
<span></span>
</template>
最后的最后
最后的最后给大家推荐几个vue3相关网址
- 第一个vue3的官网:staging-cn.vuejs.org/
- 一个集合了vue3很多相关技术栈的网址:vue3js.cn/
- 一个前端技术栈博主J技术胖:www.jspang.com/article/64#…