特性概括思维导图
响应式系统升级
响应式原理之defineProperty和Proxy
1.对对象的属性进行代理,在vue2.0中,收集依赖是通过遍历data对象里的属性方式,一个个添加defineProperty代理的, 而proxy的代理对象是整个对象,并且有很多handle方法,可以监听对象的增减等
2.defineProperty能生成的属性大概5、6个,而proxy可生成的属性相对较多
3.defineProperty不支持监听任意属性,而proxy不受影响。为什么definePrototype不能坚听数组呢?
数组的长度是可以被重写的,但是并不是每个浏览器都接受这种做法。
由于数组的长度可被重写,这就导致当一个长度为3的数组,不一定有索引为2的元素
![]()
4.defineProperty 兼容更好,IE8以上 , Proxy 需要支持ES6的浏览器,比如IE11.
defineProperty | proxy | 造成的影响 |
---|---|---|
代理对象是对象属性 | 代理对象是整个对象 | 1.不能监听属性的添加和删除添加。2.代理的时候,defineProperty更加耗费性能 |
能生成的属性少 | 能生成的属性多 | |
不能监听数组变化(下标、length,vue2.0对这部分做了处理,使得vue能监听到部分数组操作) | 能监听数组变化,而且对 Map、Set、WeakMap 和 WeakSet 的支持 |
响应式系统
整体概括【官方图】
reactive
接收一个普通对象,然后返回该普通对象的响应式代理。返回的代理对象并不等于原始对象,所以官方建议尽量操作代理对象,而不要直接操作原始对象。
如果想把响应式对象转换为原始对象可以使用toRow进行转换
可是使用isReactive判断一个对象是否由reactive创建
ref
接收一个参数,返回响应式并且可改变的对象。ref对象拥有一个指向内部值的单一属性value
如果接受的是一个对象,则会在内部使用reactive进行深层次代理后,返回一个响应式对象。
可以使用unref和isRef判断是否由ref创建 可以使用toRefs将一个ref数据转换为普通数据
toRef可以为一个reactive对象创建一个ref
watchEffect
- 调用stop()可以取消监听
- watchEffect初始化是在mounted之前,如果希望访问dom节点,需要将watchEffect放到onMounted里
- flush属性可以决定副作用执行的时机, 默认是“post”,你可以选择“sync”同步执行,或者“pre”在组件更新之前执行
- onTrack将在reactive或者ref被追踪时调用
- onTrigger会在依赖变更导致副作用触发时被调用,这两个有点类似于watch的set和get
watch
这个跟vue2.x的等效,而且跟watchEffect共享行为,还需要传入参数,我觉得不如都用watchEffect好了。如果你是vue2.x生vue3.x的,那也建议能用vue3的语法尽量用vue3的语法,虽然vue3做了兼融,但是混合模式的开发,个人感觉还是很乱的,没有充分发挥vue3的优点。
computed
这个跟vue2.x的computed有点像,有两种使用方式,一是自动派发计算属性,即传入一个ref,自动计算派发新值,二是传入set和get,让其变为一个可修改的计算属性。
生命钩子函数
生命周期的变化,主要是将beforeCreated和created去掉了,setup就可以取代他们的效果。 destory周期函数替换成了unmount
vue2.x | vue3.x |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
自定义Hooks
学到这里,真的就觉得vue3.x跟react是越来越像了。一张图告诉你自定义hooks是做什么的。
我们把模块单独抽取出来,封装成use开头的一个个独立函数,如useAdd, useDelete等,自定义hook我觉得相当于是一个封装箱,把同一个功能作用的相关数据和action都装到一个函数里,要到使用的时候单独引入,这样能使页面的代码更加简洁明了。
不过如果有的人就是觉得以类别划分会比较舒服,那也没问题,按你的习惯来就行,我使用vue2.x已经很久了,突然转到vue3.x确实也有点不适应。
Teleport
Teleport 我觉得像是一个插槽,不过以前是<slot name="xxx"/>的形式插入template,现在是在页面某个元素上定义一个id="putHere",然后<Teleport to="putHere">我是子组件</Telport>, 子组件就会被插入到id为putHere的元素里
<template>
<div class="about" id="putHere">
<!-- <h1>This is an about page</h1> -->
</div>
<div>模块2</div>
<Teleport to="#putHere">
<h1>我是Teleport</h1>
</Teleport>
</template>
表现
Suspense
很多时候我们需要按需加载组件,尤其是当数据是异步返回的时候,等待数据的过程中会有一个空白期,这个时候我们就需要设置一个默认态,以前我们是通过if else自己设置默认态来展示的,vue3.x中的Suspense就是处理这种情况的。 Suspense 是一个带有default和fallback两种状态的插槽组件,免去了我们需要额外定义一种空白状态的麻烦。
<template>
<Suspense>
<template #default>
<my-component>
<div v-if="isShow"></div>
<div v-else></div>
</my-component>
</template>
<template #fallback>
<div>Loading</div>
</template>
</Suspense>
</template>
Tree-shaking
在以前vue2.x中,有些函数是挂载在vue实例上的,Vue3.x 在考虑到 tree-shaking
的基础上重构了全局和内部 API, 表现结果就是现在的全局 API 需要通过 ES Module
的引用方式进行具名引用。
Vue.nextTick
Vue.observable
(用Vue.reactive
替换)Vue.version
Vue.compile
(仅限完整版本时可用)Vue.set
(仅在 2.x 兼容版本中可用)Vue.delete
(与上同)
Fragment
vue3.x允许组件template底下同事存在多个节点,而不再是vue2.x那般只允许存在一个根结点,如Teleport示例代码中存在多个代码片段。
看完觉得有用记得点个赞让我升一下级哦O.O