Vue3响应式

使用ref

  • 可以使用 ref 创建一个响应式的数据:
  <template>
    <div>{{ name }}</div>
  </template>

  <script setup>
    import { ref } from 'vue'
    // 现在的 name 就是一个响应式数据
    let name = ref('HuangBingQuan')
    console.log(name)
    console.log(name.value)
    setTimeout(() => {
      name.value = 'BingQuanHuang'
    }, 2000)
  </script>

  <style lang="scss" scoped></style>

ref 返回的响应式数据是一个对象,我们需要通过 .value 访问到内部具体的值。模板中之所以不需要 .value,是因为在模板会对 ref 类型的响应式数据自动解包。
ref 可以持有任意的类型,可以是对象、数组、普通类型的值、Map、Set...

对象的例子:

  <template>
    <div>{{ Person.name }}</div>
    <div>{{ Person.age }}</div>
  </template>

  <script setup>
  import { ref } from 'vue'
  // 现在的 Person 就是一个响应式数据
  let Person = ref({
    name: 'HuangBingQuan',
    age: 18
  })
  setTimeout(() => {
    Person.value.name = 'BingQuanHuang'
    Person.value.age = 20
  }, 2000)
  </script>

  <style lang="scss" scoped></style>

数组的例子

  <template>
    <div>{{ arr }}</div>
  </template>

  <script setup>
  import { ref } from 'vue'
  // 现在的 arr 就是一个响应式数据
  let arr = ref([1, 2, 3])
  setTimeout(() => {
    arr.value.push(4, 5, 6)
  }, 2000)
  </script>

  <style lang="scss" scoped></style>

第二个点,ref 所创建的响应式数据是具备深层响应式,这一点主要体现在值是对象,对象里面又有嵌套的对象:

  <template>
    <div>{{ Person.name }}</div>
    <div>{{ Person.age }}</div>
    <div>{{ Person.nested.count }}</div>
  </template>

  <script setup>
  import { ref } from 'vue'
  // 现在的 Person 就是一个响应式数据
  let Person = ref({
    name: 'Biil',
    age: 18,
    nested: {
      count: 1
    }
  })
  setTimeout(() => {
    Person.value.name = 'Biil2'
    Person.value.age = 20
    Person.value.nested.count += 2
  }, 2000)
  </script>

  <style lang="scss" scoped></style>

shallowRef API

  • 如果想要放弃深层次的响应式,可以使用 shallowRef,通过 shallowRef 所创建的响应式,不会深层地递归将对象每一层转为响应式,而只会将 .value 的访问转为响应式:
  const state = shallowRef({ count: 1});
  // 这个操作不会触发响应式更新
  state.value.count += 2;
  // 只针对 .value 值的更改会触发响应式更新
  state.value = { count: 2}

具体示例:

  <template>
    <div>{{ Person.name }}</div>
    <div>{{ Person.age }}</div>
    <div>{{ Person.nested.count }}</div>
  </template>

  <script setup>
  import { shallowRef } from 'vue'
  let Person = shallowRef({
    name: 'HuangBingQuan',
    age: 18,
    nested: {
      count: 1
    }
  })
  // 下面的更新不会触发视图更新
  setTimeout(() => {
    Person.value.name = 'HuangBingQuan'
    Person.value.age = 20
    Person.value.nested.count += 2
  }, 2000)
  // 下面的更新会触发视图更新
  setTimeout(() => {
    Person.value = {
      name: 'HuangBingQuan',
      age: 30,
      nested: {
        count: 3
      }
    }
  }, 4000)
  </script>

  <style lang="scss" scoped></style>

响应式数据的更新,带来了 DOM 的自动更新,但是这个 DOM 的更新并非是同步的,这意味着当响应式数据发生修改后,我们去获取 DOM 值,拿到的是之前的 DOM 数据:

  <template>
    <div id="container">{{ count }}</div>
  </template>

  <script setup>
  import { ref, onMounted } from 'vue'
  let count = ref(1)
  let container = null
  setTimeout(() => {
    count.value = 2 // 修改响应式状态
    console.log('第二次打印:', container.innerText)
  }, 2000)
  // 这是一个生命周期钩子方法
  // 会在组件完成初始渲染并创建 DOM 节点后自动调用
  onMounted(() => {
    container = document.getElementById('container')
    console.log('第一次打印:', container.innerText)
  })
  </script>

  <style lang="scss" scoped></style>

如果想要获取最新的 DOM 数据,可以使用 nextTick,这是 Vue 提供的一个工具方法,会等待下一次的 DOM 更新,从而方便后面能够拿到最新的 DOM 数据。

<template>
 <div id="container">{{ count }}</div>
</template>

<script setup>
 import { ref, onMounted, nextTick } from 'vue'
 let count = ref(1)
 let container = null
 setTimeout(async () => {
   count.value = 2 // 修改响应式状态
   // 等待下一个 DOM 更新周期
   await nextTick()
   // 这个时候再打印就是最新的值了
   console.log('第二次打印:', container.innerText)
 }, 2000)
 // 这是一个生命周期钩子方法
 // 会在组件完成初始渲染并创建 DOM 节点后自动调用
 onMounted(() => {
   container = document.getElementById('container')
   console.log('第一次打印:', container.innerText)
 })
 </script>

 <style lang="scss" scoped></style>

如果不用 async await,那么就是通过回调的形式:

  setTimeout(() => {
    count.value = 2 // 修改响应式状态
    // 等待下一个 DOM 更新周期
    nextTick(() => {
      // 这个时候再打印就是最新的值了
      console.log('第二次打印:', container.innerText)
    })
  }, 2000)

使用 reactive

  • reactive 通常将一个对象转为响应式对象
  <template>
    <div>{{ state.count1 }}</div>
    <div>{{ state.nested.count2 }}</div>
  </template>

  <script setup>
  import { reactive } from 'vue'
  const state = reactive({
    count1: 0,
    nested: {
      count2: 0
    }
  })
  setTimeout(()=>{
    state.count1++
    state.nested.count2 += 2;
  },2000);
  </script>

  <style lang="scss" scoped></style>

Vue 中的响应式底层是通过 ProxyAPI 来实现的,但是这个 ProxyAPI 只能对对象进行拦截,无法对原始值进行拦截。
如果拦截原始值则包装为对象,然后再使用 reactive API
ref的背后其实也调用了reactive API
原始值:Object.defineProperty
复杂值:reactive API

shallowReactive API

  • shallowReactive API,是浅层次的,不会深层次去转换成响应式。
  <template>
    <div>{{ state.count1 }}</div>
    <div>{{ state.nested.count2 }}</div>
  </template>

  <script setup>
  import { shallowReactive } from 'vue'
  const state = shallowReactive({
    count1: 0,
    nested: {
      count2: 0
    }
  })
  setTimeout(()=>{
    state.count1++
  },2000);
  setTimeout(()=>{
    state.nested.count2++
  },4000)
  </script>

  <style lang="scss" scoped></style>

使用细节

reactive局限性

  1. 使用 reactvie 创建响应式数据的时候,值的类型是有限的
  • 只能是对象类型(object、array、map、set)
  • 不能够是简单值(string、number、boolean)
  1. 不能够去替换响应式对象,否则会丢失响应式的追踪
  let state = reactive({count : 0});
  // 下面的这个操作会让上面的对象引用不再被追踪,从而导致上面对象的响应式丢失
  state = reactive({count : 1})
  1. 对解构操作不友好,当对一个 reactvie 响应式对象进行解构的时候,也会丢失响应式
let state = reactive({count : 0});
// 当进行解构的时候,解构出来的是一个普通的值
let { count } = state;
count++; // 这里也就是单纯的值的改变,不会触发和响应式数据关联的操作

// 另外还有函数传参的时候
// 这里传递过去的也就是一个普通的值,没有响应式
func(state.count)

ref解包细节

  • 所谓 ref 的解包,指的是自动访问 value,不需要再通过 .value 去获取值。例如模板中使用 ref 类型的数据,就会自动解包。
  1. ref作为reactvie对象属性
    这种情况下也会自动解包
  <template>
    <div></div>
  </template>

  <script setup>
  import { ref, reactive } from 'vue'
  const name = ref('HuangBingQuan')
  const state = reactive({
    name
  })
  console.log(state.name) // 这里会自动解包
  console.log(name.value)
  </script>

  <style lang="scss" scoped></style>

本文作者:HuangBingQuan

本文链接:https://www.cnblogs.com/bingquan1/p/18393776

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   HuangBingQuan  阅读(19)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 不将就 码农权
  2. 2 Bleeding Love 码农权
  3. 3 想你念你 码农权
Bleeding Love - 码农权
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

Not available