vue3响应式系统API介绍

下述介绍仅针对组合式API~

1. reactive

reactive是一个函数,接收一个对象,把对象数据转化为响应式对象并返回。(需要从vue中导入reactive函数)

<template>
  <div>{
    
    {
    
     state.name }}</div>
  <div>{
    
    {
    
     state.age }}</div>
  <button @click="state.name = '小华'">修改姓名</button>
</template>

<script>
import {
    
     reactive } from 'vue'
export default {
    
    
  setup () {
    
    
    const state = reactive({
    
    
      name: '小明',
      age: 18
    })
    return {
    
    
      state
    }
  }
}
</script>

我们也可以在同一个作用域下定义一个更新 state 的函数,并作为一个方法与 state 一起暴露出去:


<button @click="increment">
  {
    
    {
    
     state.count }}
</button>

<script>
import {
    
     reactive } from 'vue'
export default {
    
    
  setup() {
    
    
    const state = reactive({
    
     count: 0 })

    function increment() {
    
    
      state.count++
    }

    // 不要忘记同时暴露 increment 函数
    return {
    
    
      state,
      increment
    }
  }
}
</script>

在 setup() 函数中手动暴露大量的状态和方法是繁琐的。我们可以通过使用构建工具来简化该操作。当使用单文件组件(SFC)时,我们可以使用 < script setup > 来大幅度地简化代码。


<template>
  <button @click="increment">
    {
    
    {
    
     state.count }}
  </button>
</template>

<script setup>
import {
    
     reactive } from 'vue'

const state = reactive({
    
     count: 0 })

function increment() {
    
    
  state.count++
}
</script>

reactive的局限性(限制):

  1. 仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。

  2. 因为 Vue 的响应式系统是通过属性访问进行追踪的,因此我们必须始终保持对该响应式对象的相同引用,不可以随意地“替换”一个响应式对象,因为这将导致对初始引用的响应性连接丢失

let state = reactive({
    
     count: 0 })

// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({
    
     count: 1 })

这也意味着我们将响应式对象的属性赋值或解构至本地变量时,或是将该属性传入一个函数时,也会失去响应性:

const state = reactive({
    
     count: 0 })

// n 是一个局部变量,同 state.count
// 失去响应性连接
let n = state.count
// 不影响原始的 state
n++

// count 也和 state.count 失去了响应性连接
let {
    
     count } = state
// 不会影响原始的 state
count++

// 该函数接收一个普通数字,并且将无法跟踪 state.count 的变化
callSomeFunction(state.count)

2. ref

ref函数,接受一个简单类型或者复杂类型的传入并返回一个响应式且可变的 ref 对象(需要从vue中导入ref函数)注意:在setup函数中使用ref结果,需要通过.value 访问,模板中使用不需要加.value

<script setup>
import {
    
     ref } from 'vue'

const count = ref(0)
const objectRef = ref({
    
     count: 0 })

function increment() {
    
    
  count.value++
}
</script>

<template>
  <button @click="increment">
  	// 无需 .value
    {
    
    {
    
     count }} 
  </button>
</template>

reactive与ref如何选择?

  1. ref 函数可以接收一个简单类型的值,返回一个可改变的 ref 响应式对象,从而弥补reactive函数不支持简单类型的问题
  2. reactive和ref函数都可以提供响应式数据的转换,具体什么时候需要使用哪个API社区还没有最佳实践,大家暂时可以使用自己熟练的API进行转换
  3. 推荐只有明确知道要转换的对象内部的字段名称我们才使用reactive,否则就一律使用ref,从而降低在语法选择上的心智负担

响应性语法糖

仍处于实验性阶段,在最终提案落地前仍可能发生改动。

相对于普通的 JavaScript 变量,我们不得不用相对繁琐的 .value 来获取 ref 的值。这是一个受限于 JavaScript 语言限制的缺点。然而,通过编译时转换,我们可以让编译器帮我们省去使用 .value 的麻烦。

<script setup>
let count = $ref(0)

function increment() {
    
    
  // 无需 .value
  count++
}
</script>

<template>
  <button @click="increment">{
    
    {
    
     count }}</button>
</template>

3. toRefs

经过reactive函数处理之后返回的对象,如果给这个对象解构或者展开,会让数据丢失响应式的能力,为了解决这个问题需要引入toRefs函数,使用 toRefs函数 可以保证该对象展开的每个属性都是响应式的。(需要从vue中导入ref函数)

<template>
  <div>{
    
    {
    
     name }}</div>
  <div>{
    
    {
    
     age }}</div>
  <button @click="name = '小华'">修改姓名</button>
</template>

<script>
import {
    
     reactive } from 'vue'
export default {
    
    
  setup () {
    
    
    const state = reactive({
    
    
      name: '小明',
      age: 18
    })
    return {
    
    
      ...toRefs(state)
    }
  }
}
</script>

4. computed

计算属性来描述依赖响应式状态的复杂逻辑(需要从vue中导入ref函数)


<template>
  <p>Has published books:</p>
  <span>{
    
    {
    
     publishedBooksMessage }}</span>
</template>

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

const author = reactive({
    
    
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// 一个计算属性 ref
const publishedBooksMessage = computed(() => {
    
    
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

可写计算属性

计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 getter 和 setter 来创建:

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

const firstName = ref('John')
const lastName = ref('Doe')

const fullName = computed({
    
    
  // getter
  get() {
    
    
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    
    
    // 注意:我们这里使用的是解构赋值语法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

注意:计算属性的 getter 应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要在 getter 中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此 getter 的职责应该仅为计算和返回该值。

5.watch 侦听器

与vue2的watch功能一致。使用方式不同:
在vue3中:在setup函数中执行watch函数开启对响应式数据的监听,watch函数接收三个常规参数:

  1. 第一个参数为函数,返回你要监听变化的响应式数据
  2. 第二个参数为响应式数据变化之后要执行的回调函数
  3. 第三个参数为一个对象,在里面配置是否开启立刻执行或者深度监听
<template>
  {
    
    {
    
     name }}
  {
    
    {
    
     info.age }}
  <button @click="name = 'pink'">change name</button>
  <button @click="info.age++">change age</button>
</template>

<script>
import {
    
     reactive, toRefs, watch } from 'vue'
export default {
    
    
  setup() {
    
    
    const state = reactive({
    
    
      name: 'yx',
      info: {
    
    
        age: 18
      }
    })
    watch(() => {
    
    
      return state
    }, () => {
    
    
      // 数据变化之后的回调函数
      console.log('age发生了变化')
    }, {
    
    
      deep: true, // 开启深度监听
      immediate: true // 开启立即执行
    })
    return {
    
    
      ...toRefs(state)
    }
  }
}
</script> 

猜你喜欢

转载自blog.csdn.net/weixin_43867717/article/details/127207745