Vue3 Composition API - ref toRef 和 toRefs(笔记自用)

目录

1. 是什么?

1.1 ref

1.2 toRef 

1.3 toRefs

2. 最佳使用方式

3. 进阶,深入理解

3.1 为何需要 ref ,不能用 reactive 替代?

3.2 为何需要 .value ?

3.3 为何需要 toRef toRefs ?


扫描二维码关注公众号,回复: 14887622 查看本文章

1. 是什么?

1.1 ref

  • 生成值类型的响应式数据
  • 可用于模板 { { }} 和 reactive(在模板和 reactive 中不用.value)
  • 通过 .value 修改值
  • 命名规范xxxRef,便于理解
  • 可获取 DOM 节点
<template>
  <div>
    <p>ref demo</p>
    {
   
   { ageRef }}
    {
   
   { state.name }}
  </div>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
  name: 'RefDemo',
  setup() {
    const ageRef = ref(20) // 值类型响应式,命名xxxRef
    const nameRef = ref('张三')

    const state = reactive({
      name: nameRef
    })

    setTimeout(() => {
      console.log('ageRef', ageRef.value)
      ageRef.value = 25 // .value 修改值
      nameRef.value = '李四'
    }, 1500)
    return {
      ageRef,
      state
    }
  }
}
</script>

 获取 DOM 节点

<template>
  <p ref="elemRef">我是一行文字</p>
</template>
<script>
import { ref, onMounted } from 'vue'
export default {
  setup() {
    const elemRef = ref(null)

    onMounted(() => {
      console.log(elemRef.value, elemRef.value.innerHTML)
    })

    return {
      elemRef
    }
  }
}
</script>

1.2 toRef 

  • 针对一个响应式对象( reactive 封装)的prop(属性)
  • 用于普通对象,产出的结果不具备响应式
  • 创建一个 ref ,具有响应式
  • 两者保持引用关系(同步更改)
<template>
  <p>toRef demo</p>
  {
   
   { ageRef }} -- {
   
   { state.name }} -- {
   
   { state.age }}
</template>
<script>
import { toRef, reactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: '张三',
      age: 20
    })

    // toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式
    /* const state = {
      name: '张三',
      age: 20
    } */

    const ageRef = toRef(state, 'age')

    setTimeout(() => {
      state.age = 25 // ageRef 和 state.age 同时改变
    }, 1500)

    setTimeout(() => {
      ageRef.value = 30 // ageRef 和 state.age 同时改变
    }, 3000)

    return {
      state,
      ageRef
    }
  }
}
</script>

1.3 toRefs

  • 将响应式对象( reactive 封装)转换为普通对象
  • 对象的每个 prop(属性)都是对应的 ref
  • 两者保持引用关系
  • 合成函数返回响应式对象
<template>
  <p>toRefs demo</p>
  {
   
   { age }}--{
   
   { name }}
</template>
<script>
import { toRefs, reactive } from 'vue'
export default {
  setup() {
    const state = reactive({
      name: '张三',
      age: 20
    })

    // 将响应式对象,变成普通对象
    const stateAsRefs = toRefs(state)
    // 每个属性都是 ref 对象
    /* const { age: ageRef, name: nameRef } = stateAsRefs

    return {
      ageRef,
      nameRef
    } */

    // 返回之后,页面模板中可直接使用 name,age 属性,不需在 stateAsRefs.name ,且具有响应式
    return stateAsRefs //不能返回 { stateAsRefs }
  }
}
</script>

 合成函数返回响应式对象

2. 最佳使用方式

  • 用 reactive 做对象的响应式,用 ref 做值类型的响应式
  • setup 中返回 toRefs(state),或者 toRef(state, 'xxx')
  •  ref 变量命名 xxxRef
  • 合成函数返回响应式对象时,使用 toRefs,有助于使用方对数据进行结构

3. 进阶,深入理解

3.1 为何需要 ref ,不能用 reactive 替代?

  • 返回普通值类型,会丢失响应式
  • 如在 setup,computed,合成函数,都有可能返回值类型
  • Vue 如果不定义 ref ,用户将自造 ref ,反而混乱
<template>
  <p>why ref</p>
  {
   
   { x }}--{
   
   { y }}--{
   
   { age }}---{
   
   { ageRef }}---{
   
   { name1 }}
</template>

<script>
import { ref, toRefs, reactive, computed } from 'vue'

function useFeatureX() {
  const state = reactive({
    x: 1,
    y: 2
  })
  return toRefs(state)
}

export default {
  setup() {
    const { x, y } = useFeatureX()

    let age = 20 // 不具备响应式
    let ageRef = ref(20) // 具备响应式

    const state = reactive({
      name: '张三'
    })

    const name1 = computed(() => {
      return state.name + '啦啦啦' // 返回类似 ref 的对象,也有 .value
    })

    console.log('name1.value', name1.value)

    setTimeout(() => {
      age = 25 // 页面值不会改变
      ageRef.value = 25 // 页面值会改变
    }, 1000)

    return {
      x,
      y,
      age,
      ageRef,
      name1
    }
  }
}
</script>

3.2 为何需要 .value ?

  • ref 是一个对象(不丢失响应式),value 存储值
  • 通过 .value 属性的 get 和 set 实现响应式
  • 用于模板{ { }},reactive 时,不需要 .value ,其他情况都需要 .value
/* 定义成普通值类型,丢失响应式 */
function computed1(getter) {
  let value = 0 // 定义成普通值类型,丢失响应式
  setTimeout(() => {
    value = getter()
  }, 1500)
  return value
}

let a = 5

a = computed1(() => 1000)

// 1.5,秒之后再打印 a 的值,依然是 0,而没有变成 1000
/* 相当于
  let a = 5
  let b = a
  a = 6
  console.log(b) // 5
 */
setTimeout(() => {
  console.log(a) // 0
}, 2000)

/* 定义成 ref 类型,通过value,保留响应式 */
function computed2(getter) {
  let ref = {
    value: null // 定义成 ref 类型,通过value,保留响应式
  }
  setTimeout(() => {
    ref.value = getter()
  }, 1500)
  return ref
}

let c = {}

c = computed2(() => 1000)

// 1.5,秒之后再打印 c 的值,变成 {value: 1000}
/* 相当于
  let a = { value: 5}
  let b = a
  a.value = 6
  console.log(b.value) // 6
 */
setTimeout(() => {
  console.log(c) // {value: 1000}
}, 2000)

3.3 为何需要 toRef toRefs ?

  •  初衷:在不丢失响应式的情况下,把对象数据 分解/扩散(解构)
  • 前提:针对的是响应式对象(reactive 封装的),而非普通对象
  • 注意:不创造响应式,而是延续响应式

猜你喜欢

转载自blog.csdn.net/weixin_39763711/article/details/126606695