vue2到vue3带来了什么?

  1. 性能的提升
    打包大小减少41%
    初次渲染快55%,更新渲染快133%
    内存减少54%

  2. 源码的升级
    使用Proxy代替defineProperty实现响应式
    重写虚拟DOM的实现和Tree-Shaking

3.拥抱TypeScript
Vue3可以更好的支持TypeScript

4.新的特性
Composition API(组合API)
setup配置
新的内置组件
新的生命周期钩子

vue3 watch 踩坑

监听 reactive定义的响应式全部数据时,无法正确获取 oldValue的值
并且当使用watch监听reactive定义的响应式数据,默认强制开启了deep深度监视(关闭无效)

const params = reactive({
    catName: '踏雪',
    age: '4month',
    likes: {
        like: 'sing'
    }
})

watch(params, (newValue, oldValue) => {
    console.log('params被修改了', newValue, oldValue)
},deep: false)
// 使用reactive定义的响应式数据,监听输出的结果 newValue会被修改成新的数据, oldValue也会变成修改后的新数据
// 实际开发中如果必须监听oldValue值的变化,建议直接使用ref定义数据,不会受到影响

//当使用watch监视reactive定义的数据中的某一项时,直接使用
watch(params.catName, (newValue, oldValue)=> {

}) 
//是不行的,会抛出异常而且程序无法正常运行。需要将监听的值使用函数的形式,如下
watch(()=>params.catName, (newValue, oldValue) => {

})
// 此时监听到的数据会正常返回 newValue和oldValue,不会出现问题

 // 如果需要同时监视 reactive里定义的多个属性的变化,可以使用数组的形式
 watch([()=>params.catName, ()=>params.age], (newValue, oldValue)=> {

 })

watch(()=>params.likes, (newValue, oldValue) => {

},{deep:true})
// 当watch监视的是reactive响应式数据中的某个属性,是对象或者复杂类型的数据时,由于无法强制深度监视,层级较多时,需要手动配置deep开启深度监视

新增API watchEffect 监听

watchEffect方法默认开启 immediate:true 也就是默认第一次监听,后续只会监听在此方法中使用到的数据

watchEffect(() => {
    <!-- 此处使用到的数据才会被监听 -->
})

与watch的区别

watch的语法特点是:既要指明监视的属性目标,也要指明监视的回调
watchEffect的特点是:不用指明监视的属性目标,监视的毁掉函数中使用到了哪个属性,就监听哪个属性
watchEffect有点类似于computed的使用,但是computed侧重于计算从而得到某些东西,所以必须要写返回值,而watchEffect更加注重监视的过程执行某些方法,所以不用写返回值

vue2对比vue3生命周期变化

vue2生命周期钩子 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed
vue3生命周期钩子 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeUnmount、unmounted
区别如上,命名方面,在创建、挂载、更新都无变化,vue2中的销毁,在vue3中变成了卸载。在其他的环节做了一些小的改动和优化,使得整个流程更加智能合理

vue3中提供了compositionAPI形式的生命周期钩子,用于在setup()中使用时,与vue2中对应关系

befroeCreate ---> setup()
created ---> setup()
beforeMount ---> onBeforeMount
mounted ---> onMounted
updated ---> onUpdated
beforeDestroy ---> onBeforeUnMount
destroyed ---> onUnMounted

vue3用组合式API的形式写生命周期钩子

<!-- 引入和使用 -->
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnMount, onUnMounted }

setup() {
    onBeforeMount(() => {}) 
    onMounted(() => {}) 
    onBeforeUpdate(() => {}) 
    onUpdated(() => {}) 
    onBeforeUnMount(() => {}) 
    onUnMounted(() => {}) 
}

自定义hook函数

本质上是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2里面的mixin, 复用代码,让setup中的逻辑代码更加清晰可读性更高。将一些常用方法封装到文件中,并导出,需要提供返回值,在使用的时候导入该方法既可使用

toRef 和 toRefs

使用场景: 将响应式数据中的某个属性单独提供给外部使用时 const name = toRef(person, name)
toRefs 和 toRef类似,但是可以批量创建多个 ref对象

shallowReactive 和 shallowRef

shallowReactive定义的响应式数据只会考虑对象的第一层数据,是浅层次的响应式数据,不会往下一层创建
shallowRef用于创建基本数据类型的数据响应式化,和ref没有任何区别,但是如果传入的是复杂数据类型对象的话,不会将数据响应式化。
ref函数如果传入基本数据类型会直接创建,传入对象的话,会在底层使用reactive将数据通过Proxy代理处理为响应式数据。而shallowRef不会处理对象形式的数据

readonly 和 shallowReadonly

不希望修改响应式数据时使用
readonly 将一个响应式数据设置为只读状态,深层次的只读,不可修改
shallowReadonly 将一个响应式数据最外层设置为只读状态,不影响里面层级的修改,是浅层次的只读状态

person = readonlu(person)

toRaw 和 markRaw

原始数据无论怎么修改都不会影响到响应式数据
使用ref和reactive可以将数据处理为响应式数据
使用toRaw可以将响应式对象处理为最原始未被加工之前的原始对象,注意 toRaw只可以将使用reactive生成的响应式对象复原成原始对象,不能处理ref生成的数据。用于读取响应式对象的普通对象,对这个普通对象的所有操作,都不是响应式的,不会影响到响应式对象,所以也不会引起页面更新。
markRaw用于标记一个对象,标记过的对象将变为普通原始对象,永远都不能成为响应式对象,在有些值不应该被设置为响应式数据时使用,例如第三方类库不需要做响应式数据时,还有当渲染不可变数据源的大列表时,跳过响应式转换可以提高性能和效率

customRef

创建一个自定义ref,并对其依赖项跟踪和更新触发进行显示控制

provide 和 inject

用于祖组件与后代组件间通信,父子、祖孙都可以用,但是父子一般使用props更加方便
provide意为提供,inject意为得到

<!-- 在祖先组件中y引入 provide -->
import { provide } from 'vue'
provide('data', data)

<!-- 在孙级组件中引入 inject-->
import { inject } from 'vue'
inject('data')

对响应式数据进行判断,使用前需引入,返回值为布尔值

isRef() 方法用来检测一个值是否是ref对象
isReactive() 方法用来检测一个对象是否是Reactive创建的响应式代理
isReadonly() 方法用来检测一个对象是否是readonly创建的只读代理
isProxy() 方法用来检测一个对象是否是reactive或者readonly方法创建的代理

Composition API 比对 Options API 有什么优势

使用传统的 Options API 新增和修改数据和需求,需要不断在 data、methods、watch等一些定义的API中修改,全部拆开了,数据量庞大时,难以维护
Composition API可以更加优雅的组织代码结构,让响应式数据函数等相关代码更加有序的结合在一起,方便维护

vue3 新增组件

Fragment 译为碎片、片段

在vue2.x中,组件必须有一个根标签包裹内容,到了vue3中,允许组件不写根标签,但是会在底层将多个标签包裹在fragment虚拟元素中,可以减少标签层级,减少占用

Teleport 译为传送、瞬间移动

<!-- 标签中的元素可以通过to指定放置的位置,下面例子指将teleport中的所有dom元素放置在body元素下面 -->
<teleport to="body">
    html结构
</teleport>

Suspense 译为悬念、悬疑(vue3实验阶段)

扩展,动态引入组件

用于解决当组件嵌套过多,网络较慢时,当嵌套中的其中一个组件加载较慢时,其他组件都不会先行加载,而是等待所有组件准备完成之后一起加载,会导致页面加载较慢,或者直接加载不出来,而使用异步引入组件的方式加载组件不会存在这种问题,其他组件加载完成直接渲染,不会被阻塞。

<!-- 静态引入 -->
import Cat from './components/cat'
 
<!-- 异步引入 -->
import { defineAsyncComponent } from 'vue'  
const cat = defineAsyncComponent(() =>('./components/cat'))

当异步引入时,其中加载较快的组件已经加载完成,较慢的组件后出来,会导致组件一前一后出现,用户体验不太好,此时可以结合suspense使用,suspense类似于插槽solt,在suspense标签组件中包裹加载较慢的数据.示例如下

<suspense>
   < template v-slot:default>
        <!-- 此处 v-slot:default 中放置加载较慢的组件 -->
   </template>
   < template v-slot:fallback>
        <h2>loading加载中</h2>
         <!-- 此处 v-slot:fallback中放置组件还未加载完成时备用的组件,如loading -->
   </template>
</suspense>

vue3 移除了 filter

过滤器虽然看起来方便,但它需要一个自定义语法,打破了大括号内表达式是“只是JavaScript”的假设,这不仅有学习成本,而且有实现成本,建议用方法调用或者计算属性去替换过滤器。

vue3 router

vue3 setup中使用路由跳转时,和vue2有较大区别,vue3中没有了 this 也没有 $

<!-- vue2 -->
this.$router.push('/')

需要先引入

<!-- vue3 -->
import { useRouter } from "vue-router"
const router = useRouter()
<!-- 跳转时使用 -->
router.push('/')