vue3响应式之ref、reactive、toRef、toRefs、toRaw

ref

ref取值或赋值时需要加.value

let form = ref({age:18})
//  修改值
form.value.age = 28

ref 深层响应对象

一般使用在简单的数据类型,如:string , numberBooleanSymbol单值对象【即:只有一个属性值的对象】(如:{ count: 1 })

import { ref , isRef , shallowRef , triggerRef , customRef } from 'vue'

方案一:接收泛型

type U = { name:string }
const user = ref({name:"Fish"})    // ref( )

方案二:如果类型复杂,则使用Ref,使用Ref来自定义

import type { Ref } from 'vue'
type U = { name:string }
const user:Ref = ref({name:"Fish"})    // :Ref 

方案三:或者什么都不写,自动推断

const user = ref({name:"Fish"})    // 使用ref()进行包裹, 自动推断

修改值时,返回的是ES6的一个,有个属性.value,修改或取值(CURD)必须加.value,修改后放在value中的Proxy中。

isRef 判断是否是ref对象

返回boolean

isRef( user )    // 返回 true

shallowRef 浅层响应

shallowRef是浅层次响应,在user.value.name中,只到.value赋值,不能到.name

const user = shallowRef({ name:"Fish" })
const change = () => {
  user.value.name = 'Fisher' 
  console.log(user)                    // 值已改变
  // 
shallowRef:{{ user }}
// 视图不改变 }
const user = shallowRef({ name:"Fish" })
const change = () => {
  user.value = {name:'Fisher'} 
  console.log(user)                    // 值已改变
  // 
shallowRef:{{ user }}
// 视图也改变 }

注意:ref , shallowRef不能同时使用

否则,shallowRef会受到ref的影响,造成shallowRef视图的更新。

因为在ref底层更新逻辑时,会调用triggerRef函数。

triggerRef 强制更新收集的依赖

const user = shallowRef({name:Fish})
const change = () => {
  user.value.name = 'Fisher' 
  triggerRef(user)
  console.log(user)    // 视图将改变
}

customRef 自定义一个ref

function MyRef( value:T ){
    return customRef((track,trigger)=>{
        return {
            get(){
                track()    // 收集依赖
                return value
            }
            set( newValue ){
                console.log('触发------')
                value = newValue
                trigger()    // 触发依赖
            }
        }
    })
}

使用方法

    //  1.script
const obj = MyRef('Fish')
    //  2.视图中
{{ obj }}
    //  3.改值也需要加.value        [script]
obj.value = '修改啦'

作用:触发时,调用接口

//  简单防抖
function MyRef( value:T ){
    let timer:any
    return customRef((track,trigger)=>{
        return {
            get(){
                track()    // 收集依赖
                return value
            }
            set( newValue ){
                clearTimeout(timer)
                timer = setTimeout(()=>{
                    console.log('触发------')
                    value = newValue
                    timer = null
                    trigger()    // 触发依赖                
                },500)
            }
        }
    })
}

ref 获取dom元素






源码:core/packages/src/ref.ts

ref与reactive如何选择

使用 ref基础类型值(StringNumberBooleanSymbol) 或单值对象【即:只有一个属性值的对象】(如:{ count: 1 })

使用 reactive引用类型值(ObjectArrayMapSetWeakMapWeakSet)

ref 与reactive使用

ref 与 reactive 都是把变量变成响应式变量,使用ref或者reactive对其进行包裹

//  示例
let user.name = ref("Fish")    // ref 支持所有类型
let form = reactive("Fish")    // 不能使用string类型,所以出错
let form = reactive({name:'Fish',age:18})    // 正确表示

reactive

reactive 深层次响应

一般在复杂的数据类型使用。使用reactive取值或赋值(CURD)时无需.value

type U = { name:string,age:number }
let form = reactive({
    name:"Fish",
    age:18
})
//  修改值
form.age = 28

方案一:接收泛型

type U = { name:string,age:number }
const user = reactive({name:"Fish"})    // reactive( )

方案二:自动推断

对象





数组






如果是调用接口,点击添加,不显示?

因为reactive是一个proxy代理的对象,直接赋值的话,就覆盖proxy对象了,从而会破坏reactive的proxy对象。

所以不能直接赋值,否则破坏响应对式对象了。





解决方案一
// 直接赋值
list = res
// 修改为
list.push(...res)
解决方案二、三

添加一个对象arr,把数组作为一个属性

// 原代码
// let list = reactive([])
// 代码修改为
let list = reactive<{ arr: string[] }>({ arr: [] })

// 同时调整 
// 
  • {{ item }}
  • // 调整后内容
  • {{ item }}
  • // -------------------------------- // 原代码 // list.push(...res) // 修改为 list.arr = res // 或者修改为 list.arr.push(...res)
    
    
    
    
    
    

    readyonly 只读属性

    readonly 属性强制赋值不改变;如果原始对象变化,则改变

    let obj = reactvie({name:'Fish'})
    const read = readonly(obj)
    read.name = 'Fisher'    // 无法分配到 "name" ,因为它是只读属性。
    obj.name = 'Fisher'    // 则,obj、read 都将改变

    shallowReactive 浅层次响应

    参考shallowRef,原理相同

    toRef

    • 针对一个响应式对象(reactive 封装)的 prop(属性)创建一个ref,且保持响应式

    • 两者 保持引用关系

    toRefs

    toRefs 是一种用于破坏响应式对象并将其所有属性转换为 ref 的实用方法

    • 将响应式对象(reactive封装)转成普通对象

    • 对象的每个属性(Prop)都是对应的ref

    • 两者保持引用关系

    ref和toRef的区别

    ref

    toRef

    拷贝,修改响应式数据不会影响原始数据

    引用,修改响应式数据会影响原始数据

    ref数据发生改变,视图自动更新

    只能修改响应对象的值

    非响应式对象使用toRef视图无变化

    创建一个响应式的数据对象,传入的为基本数据类型

    接收两个参数

    第一个参数是对象,第二个参数是对象的属性

    toRef 、toRefs、toRaw

    toRef

    如果是ref 对象,直接返回;否则,创建一个类ref 对象

    类ref 对象只是做了值的改变,并未处理收集依赖触发依赖的过程。所以,普通对象无法更新视图。

    如果原始对象是响应式的,则会更新视图并改变数据

    const obj = {name:'Fish',age:18}
    // obj 为普通对象
    const res = toRef(obj,'age')
    // age 转化为响应式对象

    toRefs

    批量创建ref对象(普通对象),方便解构

    当想解构reactive对象时,记得使用toRefs,如果直接解构是没办法使用响应式的

    toRefs接收一个对象作为参数,遍历reactive对象的所有属性,使其成为ref对象,然后循环调用toRef

    const obj = reactvie({name:'Fish',age:18})
    let { name , age } = toRefs(obj)

    toRaw

    将响应式对象转化为普通对象。使用场景:不想改变视图时使用

    通过 ReactiveFlags 枚举值 取出 proxy 对象的原始对象,脱离proxy对象

    RAW = '__v_raw'

    const obj = reactvie({name:'Fish',age:18})
    const res = toRaw(obj)
    //  响应式对象转化为普通对象
    //  其实就是
    //  const res = obj['_v_raw']

    参考来源

    小满教程 https://www.bilibili.com/video/BV1dS4y1y7vd

    感谢小满,一键三连。

    你可能感兴趣的:(vue3,vue.js)