vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue3声明响应式状态使用

vue3声明响应式状态使用(含ref,reactive,toRef(),toRefs()等)

作者:朝阳39

这篇文章主要介绍了vue3声明响应式状态使用(含ref,reactive,toRef(),toRefs()等),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

Vue 3 中的数据基于 JavaScript Proxy (代理) 实现响应式

( vue2 中的数据通过 Object.defineProperty() 方法和对数组变异方法的重写,实现响应式)

选项式 API

用 data 选项声明响应式状态,值为返回一个对象的函数。

  data() {
    return {
      count: 1
    }
  },

注意事项

  data() {
    return {
      someObject: {}
    }
  },
  mounted() {
    const newObject = {}
    this.someObject = newObject

    console.log(newObject === this.someObject) // false
  }

这里原始的 newObject 不会变为响应式。

组合式 API

方式一 ref 【推荐】

所有类型的数据,都推荐使用 ref 声明响应式。

import { ref } from 'vue'

// 定义一个初始值为数字 0 的响应式变量 countRef  
const countRef = ref(0)

// 得到一个带有 .value 属性的 ref 对象
console.log(countRef ) // { value: 0 }

// 通过 .value 访问 ref 响应式变量的值
console.log(countRef.value) // 0

// 通过 .value 修改 ref 响应式变量的值
countRef.value++
<button @click="countRef++">
  {{ countRef }}
</button>
const countRef = ref(0)

// reactive 的用法详见下文
const state = reactive({
  count:countRef 
})

console.log(state.count) // 0 ,无需写成 state.count.value

state.count = 1 // 无需写成 state.count.value
console.log(countRef .value) // 1
<script setup>
import { ref, onMounted } from 'vue'

// 获取到 ref 属性为 input 的模板元素的引用(ref 变量名必须和模板里的 ref 属性值相同)
const input = ref(null)

// 生命周期:DOM 挂载完成
onMounted(() => {
  // input 输入框获得焦点(注意此处需使用.value)
  input.value.focus()
})
</script>

<template>
  <!-- ref 属性为 input 的模板元素 -->
  <input ref="input" />
</template>

因为 vue3 的响应式是通过 proxy 实现的,但 proxy 只能给对象添加响应式,无法给值类型的数据添加响应式,所以需要通过 ref() 函数先将值类型的数据包装成一个带 value 属性的 ref 对象,才能实现值类型数据的响应式。

因为 ref() 函数返回的是一个 ref 对象,响应式变量的值存储在 ref 对象的value 属性中,所以在 js 中读取和修改响应式变量的值的时候,都需要 .value ,但在以下情况中,为了方便使用,vue 对 ref 对象进行了自动解包,所以无需 .value

TS 中给 ref 声明类型

// 推导出的类型:Ref<number>
const year = ref(2020)
import { ref } from 'vue'
import type { Ref } from 'vue'

interface userInfo {
  id: number
  name: String
}

const userList: Ref<userInfo[]> = ref([])

方式二 reactive

用于创建对象类型数据的响应式。

import { reactive } from 'vue'
const state = reactive({ count: 0 })
<button @click="state.count++">
  {{ state.count }}
</button>
const raw = {}
const proxy = reactive(raw)

// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false

reactive() 的局限性

const state = reactive({ count: 0 })

// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++

其他相关 API

vue 提供了专门的API 来实现在不丢失响应式的情况下,解构响应式对象(reactive 创建),它们不创造响应式 (如 ref , reactive),而是延续响应式 (toRef , toRefs) !

基于响应式对象上的属性,创建 ref (与源属性保持同步)

const state = reactive({
  foo: 1
})

// 基于响应式对象 state 上的属性 foo ,创建 ref
const fooRef = toRef(state, 'foo')

// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2

// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3

vue3.3+ 新增了将值、refs 或 getters 规范化为 refs 的功能,详见官网

https://cn.vuejs.org/api/reactivity-utilities.html#toref

将响应式对象转换为每个属性都指向源对象相应属性的 ref 的普通对象

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

// 得到一个每个属性都是 ref 的普通对象
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// 源属性改变,Refs 对象里的 ref 属性会同步改变
state.foo++
console.log(stateAsRefs.foo.value) // 2

// Refs 对象里的 ref 属性改变,源属性会同步改变
stateAsRefs.foo.value++
console.log(state.foo) // 3

应用范例:解构对象属性,简化模板引用

<!-- 组合式API -->
<script setup>
import { reactive, toRefs } from "vue";

function getMyInfo() {
  const me = reactive({
    name: "朝阳",
    age: 35,
  });

  // 使用 toRefs 避免丢失响应式
  return toRefs(me);
}

// 通过解构赋值,便于模板中使用
let { name, age } = getMyInfo();

const changeName = () => {
  name.value = "张三";
};
</script>

<template>
  <p>姓名:{{ name }}</p>
  <p>年龄:{{ age }}</p>
  <button @click="changeName">改名</button>
</template>

判断是否为 ref

let age = ref(35)
if (isRef(age)) {
}

判断一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

判断一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。

对 ref 对象解包,是 isRef(val) ? val.value : val 的语法糖

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文