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 可以更加精细地控制哪些对象使用响应式并避免出现问题。
相关推荐
codeY1 分钟前
Vite 前端发布后「点击菜单没反应」?旧版本资源 404 的完整排查与修复实录
前端·前端工程化
90后的晨仔19 分钟前
uni-app Vue3 状态管理 Pinia 完全指南:从概念到实战的深度解析
前端
mqiqe19 分钟前
AgentScope Java 2.0 集成 Chat Completions Web:一行依赖让你的 Agent 变身 OpenAI 兼容服务
java·开发语言·前端
程序员小八77720 分钟前
后端开发初学TypeScript
前端·javascript·typescript
90后的晨仔22 分钟前
Puppeteer 与 Playwright 深度实战指南:从零到精通,全面提升开发效率
前端
sunphp开发者30 分钟前
阿里云CDN加速配置问题
前端·阿里云·云计算
小小数媒成员36 分钟前
存储器层级结构
java·服务器·前端
单线程121382 小时前
从案例分析 Vue3 Tokenizer 源码
前端·javascript·vue.js
明月_清风2 小时前
🖥️ Electron 三进程 Host 实战:从架构设计到生产落地的完整指南
前端·electron·客户端
GIS数据转换器2 小时前
智慧林草“一张图“平台
java·大数据·服务器·前端·javascript·数据库·人工智能