前言
本文主要介绍Vue3的新特性以及使用方法。从Vue3发布beta版本,到现在2个月了,还没有学习的小伙伴,要抓紧学习了。
正文
-
3.0 & 2.0 区别
- 数据驱动能力更强,重构响应式系,基于 proxy 的数据驱动能够弥补原来 definePorperty 的不足,使用Proxy的优势:
1.Proxy在使用上比Object.defineProperty方便的多
2.可直接监听数组类型的数据变化
3.Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性
4.可拦截apply、ownKeys、has等方法,而Object.defineProperty不行
5.直接实现对象属性的新增/删除
- 新增Composition API,更好的逻辑复用和代码组织
- 重构 Virtual DOM
1. 模板编译时的优化,将一些静态节点编译成常量
2. slot优化,将slot编译为lazy函数,将slot的渲染的决定权交给子组件
3. 模板中内联事件的提取并重用(原本每次渲染都重新生成内联函数)
- 代码结构调整,更有利于Tree shaking,使得体积更小
- 使用Typescript替换Flow,全面拥抱 typescript,2.x 版本无论用 class component 还是 配置 都不能很好的支持 ts.
-
3.0 新特性
生命周期 & 常用api对比
Vue2.x | Vue3.x |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount() |
mounted | onMounted() |
beforeUpdate | onBeforeUpdate() |
updated | onUpdated() |
beforeUnmount | onBeforeUnmount() |
unmounted | onUnmounted() |
data | ref()、reactive() |
watch | watch()、watchEffect() |
computed | computed() |
this.$store | useStore() |
this.$router | useRouter() |
this.$route | useRoute() |
setup()
setup 函数是 Compsition API 的入口函数,替代了我们之前的生命周期函数beforeCreate、created。
setup接受2个参数
1. props: 用来接收父组件传入的值
2. { attrs, emit, slots } = context
ref
ref 函数将我们定义的变量包装成了一个响应式的数据对象。
ref 对象拥有一个指向内部值的单一属性 .value,每次访问时我们都需要加上 .value,但在模板中使用时,它会自动解套,无需在模板内额外书写 .value
reactive()
reactive 函数是用来创建一个响应式的数据对象,类似我们之前Vue2.x的data。
区别 ref、reactive
可以看出,点击按钮后,name的值变成了lisi,但是视图并没有更新,还是zhangsan,但是ref的age却更新了。
当然,这并不是bug,原因在于一个响应型对象(reactive object) 一旦被销毁或展开(...state),其响应式特性(reactivity)就会丢失。
那么如何解决reactive展开后丢失响应式特效呢,下面会介绍 toRef、toRefs
建议:
1. 基本类型值(String 、Nmuber 、Boolean 等)或单值对象(类似像 {count: 3} 这样只有一个属性值的对象)使用 ref
2. 引用类型值(Object 、Array)使用 reactive
isref() & unref() & toRef() & toRefs()
1. isref函数用来判断一个数据是否是响应式的
const isAgeRef = isRef(age);
console.log(isAgeRef); //true
2. unref函数,接收一个ref参数,如果这个值是 ref 就返回 .value,否则原样返回
const age = ref(1);
const newAge = unref(age)
console.log(newAge) // 1
3. toRef函数将一个数据变成响应式数据
let age = 20;
let newAge = toRef(age)
console.log(age) // 20
console.log(newAge) // ObjectRefImpl {_object: 20, _key: undefined, __v_isRef: true}
4. toRefs函数就是将传入的对象里所有的属性的值都转化为响应式数据对象,解决上面reactive展开后丢失响应式问题
return {
...toRefs(state),
age,
handleClick,
};
shallowRef() & triggerRef()
通常我们使用 ref() 函数时,目的是为了引用原始类型值,例如:ref(1)。但我们仍然可以引用非基本类型值,例如一个对象:
const refObj = ref({ foo: 1 })
此时,refObj.value 是一个对象,这个对象依然是响应的,例如如下代码会触发响应:
refObj.value.foo = 2
shallowRef() 顾名思义,它只代理 ref 对象本身,也就是说只有 .value 是被代理的,而 .value 所引用的对象并没有被代理:
const refObj = shallowRef({ foo: 1 })
refObj.value.foo = 3 // 无效,值改变了,但是并不会更新试图
refObj.value = { // 有效,值改变了,并更新视图
foo:2
}
但是这样就带来一个问题,改个数据还要重新赋值,太麻烦了,这就用到了 triggerRef,我们需要在赋值后面调用依次triggerRef()函数,并把需要更新的ref传入进入,达到更新视图的目的。
refObj.value.foo = 3
triggerRef(refObj)
shallowReactive()
shallowReactive函数是用来定义一个渐层的 reactive。
每一层都用 Proxy 进行了包装
const obj = {
name: "zhangsan",
children: {
name: "lisi",
},
}
const state = reactive(obj);
console.log(state);
console.log(state.children);
只有第一层被 Proxy 处理了,也就是说只有修改第一层的值时,才会响应式更新
const shallowState = shallowReactive(obj)
console.log(shallowState);
console.log(shallowState.children);
toRaw()
toRaw函数接收代理对象作为参数,并获取原始对象。
由于经过ref和reactive包装后的对象,两者是一个引用关系,当我们修改原始值时,reactive 的值也会跟着改变,但是视图不会更新
由此可见,当我们想修改数据,但不想让视图更新时,可以选择直接修改原始数据上的值,因此需要先获取到原始数据,我们可以使用 Vue3 提供的 toRaw 方法
const obj = {
name: '前端印象',
age: 22
}
const state = reactive(obj)
const raw = toRaw(state)
console.log(obj === raw) // true
markRaw()
markRaw函数,可以将原始数据标记为非响应式的,即使用 ref 或 reactive 将其包装,仍无法实现数据响应式,其接收一个参数,即原始数据,并返回被标记后的数据
const markRawState = markRaw(obj)
const state = reactive(markRawState);
console.log(state)
// {"name":"zhangsan","children":{"name":"lisi"}}
watch() && watchEffect() && computed()
watch() && watchEffect() && computed() 函数都是用来监听数据变化的。
- watct
watch( source, cb, [options] )
参数说明:
source:可以是表达式或函数,用于指定监听的依赖对象
cb:监听到数据变化后执行的回调函数
options:可参数,可以配置的属性有 deep(深度监听)、immediate(立即触发回调函数)
watch(
() => state.collapsed,
(newVal, oldVal) => {
console.log(newVal);
console.log(oldVal);
}
);
- watchEffect
1. 不需要手动传入依赖,自动的收集依赖
2. 获取不到更新之前的值,只能拿到最新值
3. 每次初始化时会执行一次回调函数来自动获取依赖
watchEffect(()=>{
console.log(state.count);
})
1.清除副作用
watchEffect((onInvalidate)=>{
let timer = getList(title.value)
onInvalidate(()=>{
clearTimeout(timer)
执行时机:
1.在副作用即将重新执行时
2.如果在setup()或生命周期钩子函数中使用了 watchEffect, 则在卸载组件时
clearTimeout(timer)
})
})
2.异步副作用
watchEffect(async () => {
const data = await fetch()
})
3. 手动停止监听
const watcherStop=watchEffect(()=>{})
watcherStop()
- computed
const count = computed(() => props.count);
const newCount = computed({
get: () => state.count,
set: (val) => {
emit('state:count',val)
},
});
useStore() & useRoute & useRouter
import { useStore } from 'vuex'
import { useRoute, useRouter } from "vue-router";
export default defineComponent({
setup() {
const store = useStore();
const route = useRoute();
const router = useRouter();
console.log("store", store);
console.log("route", route);
console.log("router", router);
}
})
结尾
后续会继续总结Vue3的api和项目中遇到的问题。
最后,觉得文章对自己有帮助的小伙伴动手点赞哦!