面试题:说明toRefs的原理并实现

436 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路,符合活动条件。

1.关于toRefs

vue官网这样介绍:

将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref

2.为什么需要toRefs

先看一个🌰


<template>
  <div>{{count}}</div>
  <button @click="changeCount">changeCount</button>
</template>

<script lang='ts'>
import { reactive} from 'vue'

export default {
      setup() {
         let state=reactive({
           count:1
         })
         function changeCount(){
           state.count++
         }
         return {
             ...state,
             changeCount,
         }

      }

  };
</script>

我们想要的效果: 我在代码中声明了一个state响应式数据,当点击按钮的时候,count会+1

实际上:我们点击按钮的时候,count并不会累加

为什么? return出去的state,相当于

    return{
    count:1
    }

所以,我们需要一个媒介,当我们访问count值的时候,访问到state.count的值

3.先易后难

我们知道,vue3还提供了另一个api:toRef

可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

比如我们有一个state响应式数据

const state = reactive({
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

fooRef.value++
console.log(state.foo) // 2

state.foo++
console.log(fooRef.value) // 3       

这样我们通过业务逻辑修改state.count的值的时候,模板也会随之修改,那我们该如何toRef实现呢?

4.实现toRef

思路及分析

1.向toRef函数里面传递一个响应式数据state及属性foo,toRef会返回一个对象fooRef,对象上面有一个value属性

2.当读取value属性的时候,会把state.foo的值返回

3.当修改fooRef.value的值的时候,state.foo的值也会同步修改

4.当修改state.foo的值的时候,fooRef.value的值也会同步修改

实现

function toRef(obj,key){
    const warpper={
        get value(){
        //基于第二个问题,读取value,返回state.foo
            return obj[key]
        },
        set value(newValue){
        //基于第三个问题,修改时会把新的数据给到state.foo
            obj[key]=newValue
        }
    }
    //基于第一个问题,返回一个对象
    return warpper
}

基于第四个问题,修改state.foo.state.foo会改变,这是reactive做的事情

这样是不是清晰了很多了

5.实现toRefs

接下来实现toRefs,我们还拿官网的例子来说事情

const state = reactive({
  foo: 1,
  bar: 2
})

const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:

{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// ref 和原始 property 已经“链接”起来了
state.foo++
console.log(stateAsRefs.foo.value) // 2

stateAsRefs.foo.value++
console.log(state.foo) // 3

思路及分析

1.将响应式数据作为参数传递给toRefs,返回一个对象stateAsRefs,对象里面有state里面所有的属性:foo和bar

2.读取stateAsRefs.foo.value的值,会将state.foo的值返回回来

2.修改state.foo的值,stateAsRefs.foo.value的值会跟着改变

3.修改stateAsRefs.foo.value的值,state.foo的值也会跟着改变

咦,这个和toRef神似! 之前是将单个属性和响应式数据搭建一个桥梁,现在是将所有的属性和响应式数据state搭建一个桥梁

实现

遍历对象,使每一个属性都toRef一下

function toRefs(obj){
    const warpper={}
    for (const key in obj) {
       //将state所有的属性都放在wrapper里面,并将他们的值toRef,这样就可以和响应式数据的属性搭建起“桥梁”
        warpper[key]=toRef(obj,key)   
    }
    //基于第一个问题,返回了一个对象,包裹了state所有的属性
    return warpper
}

以上,toRef和toRefs就已经实现了~