本文已参与「新人创作礼」活动,一起开启掘金创作之路,符合活动条件。
一.关于ref
ref使vue3为了实现对原始值的响应而做出的方案,如何实现一个ref,感兴趣的可以看一下,ref具体的使用方式,我们贴出官网的示例代码
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
我们在vue文件中一般这样写
<template>
<div>{{count}}</div>
<button @click="changeCount"></button>
</template>
<script lang='ts'>
import { ref} from 'vue'
export default {
setup() {
let count=ref(1)
function changeCount(){
count.value++
}
return {
count,
changeCount
}
}
};
</script>
以上代码:
通过ref函数定义了一个count值,初始值是1,通过点击按钮实现count+1
我们知道:通过ref函数定义的数据,有一个特点,就是必须使用.value访问,但是我们在以.vue文件中却不需要使用.value的 方式访问,vue做了什么呢?
二.思考及分析
ref的原理:
通过obj将要代理的原始值包裹起来,键为value,值为原始值,然后通过Proxy去代理obj对象
setup函数返回的是一个对象,比如上面的代码,本质上返回的是
return{
count:ref(1),// 也是:count:{value:1 }
chnageCount:function(){
}
}
那等于说,当用户访问count属性的时候,再通过Proxy代理count的值,(ps:count值本质上是一个对象)只需要再往下访问一层.value,直接取.value的值不就行了。我们可以打印一下通过ref函数,定义的对象是啥
//test.js
const {ref}=require('vue')
let count =ref(1)
console.log(count)
结果:我们通过Proxy去代理return返回的对象,如果对象的value值是一个ref,就读取.value的值,否则就不读取,直接返回。
三.实现
function proxyObj(target){
let obj=new Proxy(target,{
get(target,key){
let value=Reflect.get(target,key)
//这里区分获取到的value是不是一个ref
if(value['_is_ref']){
return value.value
}else{
return value
}
},
set(target,key,nVal){
let value=target[key]
//修改value的时候,也要做一下判断,此时的value={value:1},如果是ref,给value.value赋值
if(value['_is_ref']){
value.value=nVal
return true
}else{
return Reflect.set(target,key,nVal)
}
}
})
return obj
}
代码分析:
以上代码通过proxy代理一个对象target,以count为例,此时代理的时一个对象{count:{value:1,"_is_ref":true} }
1.当我们读取属性是count的时候,会触发get函数,此时我们去判断target[key]是否有_is_ref属性,如果有,我们把value的值取出,给到count
2.当我们修改count属性值的时候,会触发set函数,我们依旧要判断一下,是不是有_is_ref属性,如果有,那我门将新值nval赋值给.vaue,而不是{value:1,"_is_ref":true}。
这样我们在template模板中就可以不使用.value去访问了