vue3.0(六) toRef,toValue,toRefs和toRow,markRaw

文章目录


toRef

  • toRef 函数可以将一个响应式对象的属性转换为一个独立的 ref 对象。
  • 返回的是一个指向源对象属性的 ref 引用,任何对该引用的修改都会同步到源对象属性上。
  • 使用 toRef 时需要传入源对象和属性名作为参数。
  • 转换后的响应式可以被用于计算属性及监听器中
  1. 原对象是响应式,改变后,值改变,页面也会更新

    cpp 复制代码
    <template>
      <div>{{ state.bar }}</div>
      <div>{{ barState }}</div>
      <button @click="mutateDeeply">修改数据</button>
    </template>
    <script lang="ts">
    import { defineComponent, reactive, toRef } from 'vue'
    export default defineComponent({
      setup () {
        const state = reactive({
          bar: 2
        })
        const barState = toRef(state, 'bar')
        function mutateDeeply () {
          barState.value++
          console.log(barState.value, state.bar)
        }
        return {
          mutateDeeply,
          state,
          barState
        }
      }
    })
    </script>
  1. 原对象为非响应式,改变后,值会改变,但页面不会更新

    cpp 复制代码
    <template>
      <div>{{ state.bar }}</div>
      <div>{{ barState }}</div>
      <button @click="mutateDeeply">修改数据</button>
    </template>
    <script lang="ts">
    import { defineComponent, reactive, toRef } from 'vue'
    export default defineComponent({
      setup () {
        const state = {
          bar: 2
        }
        const barState = toRef(state, 'bar')
        function mutateDeeply () {
          barState.value++
          console.log(barState.value, state.bar)
        }
        return {
          mutateDeeply,
          state,
          barState
        }
      }
    })
    </script>
  1. 接收一个 Reactive 数组,此时第二个参数应该传入数组的下标:
cpp 复制代码
  // 这一次声明的是数组
 const words = reactive([1, 2, 3])
  
  // 通过下标 `0` 转换第一个 item
  const a = toRef(words, 0)
  console.log(a.value) // 1
  console.log(words[0]) // 1
    
  // 通过下标 `2` 转换第三个 item
  const c = toRef(words, 2)
  console.log(c.value) // 3
  console.log(words[2]) // 3
  1. 设置默认值
  • 如果 Reactive 对象上有一个属性本身没有初始值,也可以传递第三个参数进行设置(默认值仅对 Ref 变量有效):

  • 数组也是同理,对于可能不存在的下标,可以传入默认值避免项目的逻辑代码出现问题:

    cpp 复制代码
    interface Member {
      id: number
      name: string
      // 类型上新增一个属性,因为是可选的,因此默认值会是 `undefined`
      age?: number
    }
     
    // 声明变量时省略 `age` 属性
    const userInfo: Member = reactive({
      id: 1,
      name: 'Petter',
    })
     
    // 此时为了避免程序运行错误,可以指定一个初始值
    // 但初始值仅对 Ref 变量有效,不会影响 Reactive 字段的值
    const age = toRef(userInfo, 'age', 18)
    console.log(age.value)  // 18
    console.log(userInfo.age) // undefined
     
    // 除非重新赋值,才会使两者同时更新
    age.value = 25
    console.log(age.value)  // 25
    console.log(userInfo.age) // 25
    
    const numbers = reactive([1, 2, 3])
     
    // 当下标对应的值不存在时,也是返回 `undefined`
    const d = toRef(words, 3)
    console.log(d.value) // undefined
    console.log(words[3]) // undefined
     
    // 设置了默认值之后,就会对 Ref 变量使用默认值, Reactive 数组此时不影响
    const e = toRef(words, 4, 'e')
    console.log(e.value) // e
    console.log(words[4]) // undefined

toValue

  • toValue() 是一个在 3.3 版本中新增的 API。它的设计目的是将 ref 或 getter 规范化为值。如果参数是 ref,它会返回 ref 的值;如果参数是函数,它会调用函数并返回其返回值
  • 与 unref() 类似,不同的是此函数也会规范化 getter 函数。如果参数是一个 getter,它将会被调用并且返回它的返回值。
  1. 应用

    cpp 复制代码
    console.log(toValue(1), toValue(ref(1)), toValue(() => 1)) // 1 1 1
  2. 在组合式函数中规范化参数:

cpp 复制代码
<template>
  <button @click="mutateDeeply">修改数据</button>
</template>
<script lang="ts">
import { defineComponent, toValue, ref, watch } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
export default defineComponent({
  setup () {
    const num = ref(2) 
    function mutateDeeply () {
      num.value++
      useFeature(num)
    }

    function useFeature (id: MaybeRefOrGetter<number>) {
      // 监听数据变化
      watch(() => toValue(id), newId => {
        // 处理 id 变更
        console.log(newId)
      })
    }
    // 这个组合式函数支持以下的任意形式:
    useFeature(1)
    useFeature(ref(1))
    useFeature(() => 1)
    return {
      mutateDeeply,
      num
    }
  }
})
</script>

toRefs

  • toRefs 函数可以将一个响应式对象转换为一个普通的对象,该对象的每个属性都是独立的 ref 对象。
  • 返回的对象可以进行解构,每个属性都可以像普通的 ref 对象一样访问和修改,而且会保持响应式的关联。
  • toRefs 的使用场景主要是在将响应式对象作为属性传递给子组件时,确保子组件可以正确地访问和更新这些属性。
  1. 与 toRef 不同, toRefs 只接收了一个参数,是一个 reactive 变量。

    cpp 复制代码
    <script lang="ts">
    import { defineComponent, toRefs, reactive } from 'vue'
    
    export default defineComponent({
      setup () {
        interface user {
          id: number
          name: string
        }
        
        // 声明一个 Reactive 变量
        const userInfo: user = reactive({
          id: 1,
          name: 'Petter'
        })
        
        // 传给 `toRefs` 作为入参
        const userInfoRefs = toRefs(userInfo)
        console.log(userInfoRefs)
        console.log(userInfoRefs.id.value)
        console.log(userInfoRefs.name.value)
        return {
        }
      }
    })
    </script>

可以参考vue3.0(五) reactive全家桶

toRow

Vue 创建的代理返回其原始对象。

toRaw() 可以返回由 reactive()、readonly()、shallowReactive() 或者 shallowReadonly() 创建的代理对应的原始对象。

是一个可以用于临时读取而不引起代理访问/跟踪开销 ,或是写入而不触发更改的特殊方法

不建议保存对原始对象的持久引用,请谨慎使用。

cpp 复制代码
import { reactive, toRaw } from 'vue'
const state = reactive({
 count: 0
})
// 获取原始对象
const rawState = toRaw(state)
// 验证原始对象与包装后的对象是否相等
console.log(rawState === state) // false
// 改变原始对象的值
rawState.count += 1
// 验证包装后的对象是否也受到了改变

markRaw

将一个对象标记为不可被转为代理。返回该对象本身。

markRaw 方法可以将一个对象标记为"非响应式",从而使其不会被 reactive 包裹,也就不会成为 Vue3 中的响应式对象。

cpp 复制代码
import { reactive, markRaw } from 'vue'
 
const state = reactive({
  count: 0,
  obj: {
    name: '张三'
  }
})
 
// 标记 obj 对象为"非响应式"
markRaw(state.obj)
// obj 对象不再被 reactive 包裹
console.log(state.hasOwnProperty('__v_raw')) // false
 
// 赋新值时不会触发响应更新
state.obj.name = '李四'
console.log(state.obj.name) // 李四

需要注意的是,一旦一个对象被标记为"非响应式",它就无法再被 reactive 进行包裹成为响应式对象。

在使用 markRaw 方法时,需要确保这个对象在后续的代码中不需要作为响应式对象来使用或者监听其变化

toRef和toRefs的区别

  1. toRef 修改的是对象的某个属性,生成一个单独的 ref 对象。
  2. toRefs 修改的是整个对象,生成多个独立的 ref 对象集合。
  3. toRefs 适用于在组件传递属性或解构时使用,更加方便灵活,而 toRef 更适合提取单个属性进行操作。
  4. toRef 和 toRefs 都用于将响应式对象的属性转换为 ref 对象。
  5. 转换后的属性仍然保持响应式,对属性的修改会反映到源对象上。
  6. 不管是使用 toRef 还是 toRefs 将响应式对象转成普通对象,在 script 中修改和访问其值都需要通过 .value 进行。

toRaw 和markRaw的用处

  1. toRaw 可以还原数据并避免意外的响应式行为,
  2. markRaw 可以更加精细地控制哪些对象使用响应式并避免出现问题。
相关推荐
狂炫一碗大米饭几秒前
Event Loop事件循环机制,那是什么事件?又是怎么循环呢?
前端·javascript·面试
IT、木易2 分钟前
大白话Vue Router 中路由守卫(全局守卫、路由独享守卫、组件内守卫)的种类及应用场景
前端·javascript·vue.js
用户63326312819993 分钟前
Kotlin协程:Continuation 和 suspend 函数的编译后逻辑
javascript
顾林海3 分钟前
JavaScript 变量与常量全面解析
前端·javascript
程序员小续3 分钟前
React 组件库:跨版本兼容的解决方案!
前端·react.js·面试
乐坏小陈4 分钟前
2025 年你希望用到的现代 JavaScript 模式 【转载】
前端·javascript
生在地上要上天4 分钟前
从600行"状态地狱"到可维护策略模式:一次列表操作限制重构实践
前端
oil欧哟6 分钟前
🥳 做了三个月的学习卡盒小程序,开源了!
前端·vue.js·微信小程序
奶球不是球10 分钟前
el-table(elementui)表格合计行使用以及滚动条默认样式修改
前端·vue.js·elementui
liuyang___12 分钟前
vue管理布局左侧菜单栏NavMenu
前端·javascript·vue.js