前述
11/9 晴
hello,大家周五好,过完今天又迎来一个愉快的双休拉。老样子,开始写文章之前,先来唠唠嗑。可能关注我的小伙伴会发现,我这次更新文章的频率相对而言是很快的。距离上次写,才过去了两周。
为什么要提上更新频次呢?
那就是:拒绝懒惰,想把一件事做好,还是需要坚持呀。还有写文章,不但可以给大家分享,也可以自我巩固和学习。
然后今天要说的内容是需要大家对 vue 有一定了解的哦!
好了,开始今天的正文吧,走起~
正文
vue3响应式
说到 vue3 的响应式,那就先来说下 vue2 的响应式。vue2 中的响应式,直接在 data 中进行数据处理和绑定,return 值,就可以做到了。但是在 vue3 中可以说是大改了 vue2 的处理机制。
在 vue3 中不再使用 data ,而是改用 setup ,以及 ref ,reactive 的方法来做到响应式。
那么 vue2 中的响应式机制是怎么做的呢?看代码:
Object.defineProperty(data,'count',{
get(){},
set(){}
})
上面这段代码,相信大家都不陌生,get,set 进行数据的读取和修改。但就这种方式可以看出,只能对于已有的数据进行这两个操作,且不能满足其他的需求。比方说:
如果我有一个对象。我需要对这个对象添加一个新的属性,或者删除一个已有的属性。上面的方法是不是就不会有作用,也做不到响应式。
再者,如果我有一个数组,我需要把这个数组变成一个响应式的数组,我需要怎么办?是不是就只能重写这个数组,更新这个数组中的一系列需要更新的元素,
再者,如果我是直接通过下标来更新某个元素,会响应式吗?明显也是不会的。所以这也是为什么需要使用 $set() 来对这种情况进行响应式操作。
那么 vue3 ,完完全全就不是像 vue2 中这样玩儿的了。而是通过 Proxy(代理)以及 Reflect(反射),对数据进行监视以及响应式的操作。
在看 vue3 的响应式是怎么做的之前呢,可以先来简单看下这两个方法。Proxy,Reflect。
Proxy
如何去理解 Proxy 呢?官方的术语比较晦涩,其实简单点理解就是一句话:实现数据代理。
为了方便,我把语法和相关解释都贴一下:
语法:
const p = new Proxy(target, handler)
-
handler 包含捕捉器(trap)的占位符对象,可译为处理器对象。
-
traps
提供属性访问的方法。这类似于操作系统中捕获器的概念。
-
target
被 Proxy 代理虚拟化的对象。它常被作为代理的存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。
这里简单扯一下:target:就是你要代理的目标对象。handler:就是监视你这个目标对象的处理器,它也是个对象,包含多个方法,看需要使用即可。
Reflect
同样,如何去理解 Reflect 呢?也可以简单理解成一句话:就是动态的对被代理的对象进行操作。并且它是配合 Proxy 中的 handler 来一起使用的。
有一个注意点,就是这个方法,不能够实例,也就是不能够 new。
哈哈哈哈,感觉说了这么多,还不如来看一段代码实际:
//这边就不写全部的操作细节了,大家可以自己去操作下
// 目标对象
const usr ={
name:haha,
age:30,
child:{
name:xixi,
age:18
}
}
// 代理对象 需要传入两个参数,并且是对象。第一个就是目标对象,第二个就是处理器对象
const pro = new Proxy(usr,{
// 读取
get(target, property){
return Reflect.get(target, property)
},
// 修改
set(target, property,value){
return Reflect.set(target, property,value)
}
// 删除
deleteProperty(target, property){
return Reflect.deleteProperty(target, property)
}
// ... 其他的方法
})
// 获取对象中的属性
pro.name // haha
pro.child.name // xixi
// 更改对象
pro.name='wawa' // wawa
pro.child.age = 100 // 100
// 新增
pro.score = 98 // 对象usr中新增了一个score属性
// 删除
delete pro.name // 删除了name
detele pro.child.name // 删除了child种的name
要注意一点就是,每个方法中一定要对应 Reflect ,因为它是完成响应式的关键,要是没有它,你所有的操作,都无法作用到目标对象上。
看到这边,小伙伴们有没有想过一个问题,为什么 vue3 的响应式要变?
可能有小伙伴会说,啊,vue2 中的 data ,return ,那么方便,现在搞到 vue3 中,还要 setup,reactive,ref。这不是繁琐操作了吗?
为什么呢?小伙伴们可以看到 vue2 中是通过 Object.defineProperty,的方式实现响应式。那么这个有什么弊端呢?可以简单的思考几分钟。
答案就是:Object.defineProperty 这种形式,会给对象中的每个属性都加上一个 get,set 方法来达到响应式的目的。那么这个其实就会带来开销的问题。
简单点说:如果某个对象下面有n个属性,那么是不是就要开辟2倍的n 的 get 和 set。所以这样做的弊端是可想而知。
而在 vue3 中,只需要一个代理对象,一个 Reflect 对象,就可以实现响应式,并且没有额外的开销。而且,在上面的举例中,可以看到,这种代理是深度代理,就是响应式,不仅可以响应一级属性,甚至可以响应多集属性,这也就解释了,为什么 reactive({}),是深度代理。(当然也有浅代理的方法,今天不讲这块儿)。
好了,响应式说完,那就聊聊关于 setup,ref,reactive 的一些细节。
setup
生命周期
setup 是在 BeforeCreate 之前执行的,这个在 vue3 的生命周期中写的很清楚,那么这里需要注意什么呢?
就是说,在 setup 执行的时候,组件还没有创建,那么就是还没有实例化,是不是就说明, this 这个时候,还是 undefined ?
答案是的。小伙伴可以去生命周期中,console.log看一下便知了。
合并
vue3 中是可以写 vue2 中的代码,这个小伙伴肯定都知道,那么如果一个 vue 文件中,既有 vue3 的 setup 又有 vue2 的 data,methods 的话,会怎么样呢?
这不简单吗。写段代码看下不就知道了吗:
setup(){
const a =10 ;
const aaa = ()=>{
console.log('hhhhhh')
}
return{
a,
aaa
}
},
data(){
return {
b:20
}
},
methods:{
bbb(){
console.log('xixixixi')
}
},
mounted(){
console.log(this) // 打印看看
}
// console输出:
Proxy:Object
handler:Object
Target:{
a:10,
b:20,
aaa(),
bbb()
}
看到这,相信小伙伴就知道了,那就是,如果 vue 文件中既有 vue3 的代码,又有 vue2 的代码,那么就会被合并成一个组件对象属性,统一交给 Proxy 代理对象。
说到这,可能小伙伴要笑了,那我就写个 setup ,其他都用 data 来做,岂不是美哉。哈哈哈哈,我没试过,有想法的可以试试。
还有就是 setup 本事就是 return,所以不能用异步,也就是说像 async steup(){} ,这样,因为异步返回的就是 promise 了。所以是不可以的。
好了,关于 setup 相关的就说这么多,接下来简单说下,ref 和 reactive。
ref & reactive
关于 ref 和 reactive 小伙伴们可能都知道,一个是处理数据,一个是处理对象,但其实 ref 也可以处理对象。只不过在调用的时候,需要使用 .value 的方式来对数据进行操作。
并且 ref 中如果是放入了对象,那就会经过 reactive 的处理,生成 proxy 代理对象。这个代理也是深度代理。嵌套层级再多也可以。
这里的代码演示也很简单,只需要定义再去console.log 看一下就可以了
const a = ref(1)
const aa = ref({name:'haha',age:18})
const aaa = reactive({score:88})
console.log(a,aa,aaa)
// 打印结果
aa中会存在Proxy:Object
关于这块也就这么多啦,嘿嘿,最后,留一个小问题,给大家思考一下。往下看👇
template模板
为什么vue3中的vue文件中不要求,一定要在template中写上div根标签呢?
可以评论区告诉我~
结语
以上就是本期说的内容啦,要是有说的不对的地方,还请指出。
然后关于上一篇文章呢,说到遇到了瓶颈期。其实现在也想通了,好好做自己的事情,踏踏实实的做好自己的事情,一切交给机会和缘分。
好啦,这期就说到这咯,要是有小伙伴想听其他的,也可以留言。
嘿嘿~ 拜拜~