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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
``` 以下是两种关于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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
watchEffect
watch是:既要指明监视的属性,也要指明监视的回调。
watchEffect是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
这个函数的功能和计算属性差不多,但是
computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
<script>import {watchEffect} from "vue";export default { setup() { watchEffect(() => { if (state.value) { // do something ... setDates(state.value) } }) }}
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
- itemRefs 不必是数组:它也可以是一个对象,其 ref 会通过迭代的 key 被设置。- 如果需要,itemRef 也可以是响应式的且可以被监听。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
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>
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
响应式
添加响应性: 为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 或 reactive。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>0
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>1
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
修改响应式 property: 当使用响应式 provide / inject 值时,官方中
建议尽可能将对响应式 property 的所有修改限制在定义 provide 的组件内部。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>2
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
如果要确保通过 provide 传递的数据不会被 inject 的组件更改,官方建议对提供者的 property 使用 readonly。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>3
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
8、
9、
TIP
通过 v-html 创建的 DOM 内容不会被作用域样式影响,但你仍然可以使用深度选择器来设置其样式。
module
自定义注入名称:
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>4
与组合式 API 一同使用: 注入的类可以通过 useCssModule API 在 setup() 和
这个语法同样也适用于
单文件组件状态驱动的 CSS 变量 (10、getCurrentInstance
getCurrentInstance()
是Vue3.x中的核心方法,用于访问实例上下文的router及vuex等。
概述:一个很重要的方法,获取当前组件的实例、上下文来操作router和vuex等。
引入:由vue提供,按需引入:import { getCurrentInstance} from 'vue';
使用:获取当前组件的上下文,推荐使用:const { proxy } = getCurrentInstance()。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>5
二、vue3周边生态
1、vue-router
vue2.x中,可以通过this.$router或者this.$route来获取或者操作路由。
在vue3.0中,引入了Composition-api。在setup函数中无法使用this获取组件实例。新版本的vue-router也提供了支持Composition-api的hooks,例如useRouter,useRoute函数。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>6
另外,vue-router还提供了支持的Composition-api的两个路由守卫:update and leave
beforeRouteLeave:离开当前页面路由时触发,return false则阻止跳转,next中不能写参数
beforeRouteUpdate:动态路由 只有参数发生变化是才执行(通俗理解及跳转页面时)
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>7
2、vuex
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>8
三、其他
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
中添加我们需要传送到的位置。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
将编写的组件包装在 teleport 组件中,还需要指定一个 to 属性,为该属性分配一个查询选择器,以标识目标元素。
<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>0
2、Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
试验性
Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。特此声明,以便社区能够为当前的实现提供反馈。
生产环境请勿使用。
以上是官方的警告!
使用:
首先我们先写一个异步组件 注意点:如果你要使用Suspense的话,要返回一个promise对象,而不是原来的那种JSON对象。
<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>1
使用suspense组件
<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>2
处理异步请求错误
在异步请求中必须要作的一件事情,就是要捕获错误,因为我们没办法后端给我们返回的结果,也有可能服务不通,所以一定要进行捕获异常和进行处理。
在vue3.x的版本中,可以使用onErrorCaptured这个钩子函数来捕获异常。在使用这个钩子函数前,需要先进行引入.
<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>3
有了onErrorCaptured就可以直接在setup()函数中直接使用了。钩子函数要求我们返回一个布尔值,代表错误是否向上传递。这里的false一方面表示:错误不会冒泡给父组件;另一方面表示vue将停止该错误的传播。
3、片段(Fragment)
在 Vue2.x 中, template中只允许有一个根节点:
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
但是在 Vue3.x 中,你可以直接写多个根节点:
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
最后的最后
最后的最后给大家推荐几个vue3相关网址
第一个vue3的官网:https://staging-cn.vuejs.org/
一个集合了vue3很多相关技术栈的网址:https://vue3js.cn/
一个前端技术栈博主J技术胖:https://www.jspang.com/article/64#toc0
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
10、getCurrentInstance
getCurrentInstance()
是Vue3.x中的核心方法,用于访问实例上下文的router及vuex等。
概述:一个很重要的方法,获取当前组件的实例、上下文来操作router和vuex等。
引入:由vue提供,按需引入:import { getCurrentInstance} from 'vue';
使用:获取当前组件的上下文,推荐使用:const { proxy } = getCurrentInstance()。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>5
二、vue3周边生态
1、vue-router
vue2.x中,可以通过this.$router或者this.$route来获取或者操作路由。
在vue3.0中,引入了Composition-api。在setup函数中无法使用this获取组件实例。新版本的vue-router也提供了支持Composition-api的hooks,例如useRouter,useRoute函数。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>6
另外,vue-router还提供了支持的Composition-api的两个路由守卫:update and leave
beforeRouteLeave:离开当前页面路由时触发,return false则阻止跳转,next中不能写参数
beforeRouteUpdate:动态路由 只有参数发生变化是才执行(通俗理解及跳转页面时)
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>7
2、vuex
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>8
三、其他
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
中添加我们需要传送到的位置。
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
将编写的组件包装在 teleport 组件中,还需要指定一个 to 属性,为该属性分配一个查询选择器,以标识目标元素。
<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>0
2、Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验。
试验性
Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。特此声明,以便社区能够为当前的实现提供反馈。
生产环境请勿使用。
以上是官方的警告!
使用:
首先我们先写一个异步组件 注意点:如果你要使用Suspense的话,要返回一个promise对象,而不是原来的那种JSON对象。
<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>1
使用suspense组件
<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>2
处理异步请求错误
在异步请求中必须要作的一件事情,就是要捕获错误,因为我们没办法后端给我们返回的结果,也有可能服务不通,所以一定要进行捕获异常和进行处理。
在vue3.x的版本中,可以使用onErrorCaptured这个钩子函数来捕获异常。在使用这个钩子函数前,需要先进行引入.
<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>3
有了onErrorCaptured就可以直接在setup()函数中直接使用了。钩子函数要求我们返回一个布尔值,代表错误是否向上传递。这里的false一方面表示:错误不会冒泡给父组件;另一方面表示vue将停止该错误的传播。
3、片段(Fragment)
在 Vue2.x 中, template中只允许有一个根节点:
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
但是在 Vue3.x 中,你可以直接写多个根节点:
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9
最后的最后
最后的最后给大家推荐几个vue3相关网址
第一个vue3的官网:https://staging-cn.vuejs.org/
一个集合了vue3很多相关技术栈的网址:https://vue3js.cn/
一个前端技术栈博主J技术胖:https://www.jspang.com/article/64#toc0
<template><div> <h2>{{ title }}</h2> <h2>{{ rTitle }}</h2> <div @click="hanldeClick">点他</div></div></template>9