使用Proxy代替defineProperty实现响应式(数据劫持)
defineProperty的缺陷:
- 不能监听数组变化
- 只能劫持对象的属性而不是对象本身,给对象添加属性时无法检测到
Proxy弥补了这两种缺陷: “Proxy用于修改某些操作的默认行为,等同于对编程语言直接做出修改,属于‘元编程(meta progtamming)’, 即对编程语言进行编程”
更好地适用TS
Tree shaking
将全局API分块,如果未被使用,则不会被打包进生产代码。 典型例子就是在Vue3中computed, watch, onMounted都需要引入,如果未引入则不会被打包
Fragment
支持多个根节点,即template标签中可以有多个div标签了。
新的特性 Composition API
setup配置,ref与reactive...
//引入的不再是Vue构造函数,而是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
//创建应用实例对象:app。类似于之前Vue2中的vm,但app比vm属性更少,更轻量级
const app = createApp(App)
//挂载
app.mount('#app')
简写Vue3中的数据绑定
//源数据,(下面的p是代理数据
let person = {
name: 'sxy',
age: 23
}
//Vue2中的实现响应式(用于对比)
let p = {}
Object.defineProperty(p, 'name', {
configurable: true,
get(){//有人读取name时
return person.name
},
set(){//有人修改name时调用
person.name = value
}
})
//模拟Vue3中实现响应式.使用ES6中的Proxy和Reflect
const p = new Proxy(person, {
//有人读取p的某个属性时调用
get(target, propName){
console.log(`有人读取了p身上的${propName}属性`)
return Reflect.get(target, propName)
},
//有人修改(增加)p的某个属性
set(target, propName, value){
console.log(`有人修改了p身上的${propName}属性,更新界面`)
},
//有人删除p的某个属性时调用
deleteProperty(target, propName){
console.log(`有人删除了p身上的${propName}属性,更新界面`)
return Reflect.deleteProperty(target, propName)
}
})
setup的两个注意点
-
setup的执行时机
- 在
beforeCreate之前执行一次,this是undefined
- 在
-
setup的参数
-
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
-
context:上下文对象
- attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于
this.$attrs - slots:收到的插槽内容,相当于
this.$slots - emit:分发自定义事件的函数,相当于
this.$emit
- attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于
-
计算属性的写法
import {reactive, computed} from 'vue'
export default {
name: 'Demo',
setup(){
//数据
let person = reactive({
firstName: '张',
lastName: '三'
})
//计算属性
let fullName = computed(() => {
return person.firstName + '-' + person.lastName
})
//返回一个对象
return {
person,
fullName
}
}
}
监视属性的写法
和Vue2并无太大区别。两个注意点
- 对于reactive定义的对象数据。oldValue不能正确获取。
- 对于reactive定义的对象数据。强制开启深度监视。
import {ref, reactive, watch} from 'vue'
export default {
name: 'App',
//Vue2中的写法
watch:{
//watch的简写形式
// sum(newValue, oldValue){
// console.log('sum的值变化了', newValue, oldValue);
// }
//完整形式,可以设置配置项
sum:{
immediate: true,
deep: true,
handler(newValue, oldValue){
console.log('sum的值改变了', newValue, oldValue);
}
}
},
setup(){
let sum = ref(0)
let msg = ref('Hello')
let person = reactive({
name: 'sxy',
age: 23
})
//Vue3中的监视。参数1是监视的数据,参数2是监视的回调,参数3是监视的配置项
//情况1:监视ref所定义的一个响应式数据
watch(sum, (newValue, oldValue)=>{
console.log('sum变了', newValue, oldValue);
})
//情况2:监视ref所定义的多个响应性数据
watch([sum, msg], (newValue, oldValue)=>{
console.log('sum或msg变了', newValue, oldValue);
})
//情况3:监视reactive所定义的一个响应式数据.
//此时无法正确获取oldValue。解决方法就是把数据拆开单独使用ref
//并且强制开启深度监视
watch(person, (newValue, oldValue)=>{
console.log('person变了', newValue, oldValue);
})
//情况4:监视reactive所定义的一个响应式数据中的某一个属性
watch(() => person.age, (newValue, oldValue) => {
console.log('person的age变化了', newValue, oldValue);
})
//情况5:监视reactive所定义的一个响应式数据中的多个属性
watch([()=>person.name, ()=>person.age], (newValue, oldValue)=>{
console.log('person的name或age变化了', newValue, oldValue);
})
//特殊情况。
//由于此时监视的是reactive定义的对象中的某个属性,并且这个属性依然是一个对象,所以deep配置有效
watch(() => person.job, (newValue, oldValue) => {
console.log('person的job变化了', newValue, oldValue);
}, {deep: true})
return {
sum,
msg,
person
}
}
}
Vue3生命周期
2个改变
beforeDestroy()和destroyed()改名为beforeUnmount()和unmounted()- 挂载之后才会开始生命周期。Vue2中会在
created()之后检测是否挂载
除此之外,Vue3提供了Composition API形式的生命周期钩子,即在setup()中使用.
-
beforeCreated()和created()没有对应的Composition API形式 -
其它增加
ononBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmounted
//配置项形式写法 beforeMount() { ... } //Composition API格式 onBeforeMount(() => { ... })
自定义hook函数
一个用于复用的工具函数。把setup函数中使用的Composition API进行了封装
toRef
创建一个ref对象,其value值指向另一个对象中的某个属性
toRefs和toRef功能一致
\