崇序猿的取经之路 vue3 (二)

181 阅读3分钟

1666611198148.jpg

 程序猿要了3个孩子,分别取名叫Ctrl、Alt 和Delete,
 如果他们不听话,程序猿就只要同时敲他们一下就会好的。

一 computed

1. 变成函数式了,不再和之前一样了,之前为一个对象

1.1 vue2 版本

    var vm = new Vue({
      data: { a: 1 },
      computed: {
        // 仅读取  不能直接修改 
        aDouble: function () {
          return this.a * 2
        },
        // 读取和设置
        aPlus: {
          get: function () {
            return this.a + 1
          },
          set: function (v) {
            this.a = v - 1
          }
        }
      }
    })
    vm.aPlus   // => 2
    vm.aPlus = 3
    vm.a       // => 2
    vm.aDouble // => 4

1.2 vue3 版本

简写版本 仅读取 不能直接修改

    接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 `ref` 对象
    const count = ref(1)
    const plusOne = computed(() => count.value + 1)

    console.log(plusOne.value) // 2

    plusOne.value++ // 错误

在只读的情况下修改内容的话,会警告 写操作失败:计算值为只读

1647778763(1).png

或者

接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象。

    const count = ref(1)
    const plusOne = computed({
      get: () => `count.value` + 1,
      set: val => {
        `count.value` = val - 1
      }
    })

    `plusOne.value` = 1
    console.log(count.value) // 0
   
   主要 以上都为 ref(Refimpl) 对象,都是需要进行 `.value`

在vue3中 新增了ts版本,这里就不写了 computedTs写法

 总体来说 没什么特别大的变化,最大的变化还是语法上的变化

支持的新特性 获取上一个值

  • 仅 3.4+ 支持

如果需要,可以通过访问计算属性的 getter 的第一个参数来获取计算属性返回的上一个值

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

const count = ref(2)

// 这个计算属性在 count 的值小于或等于 3 时,将返回 count 的值。
// 当 count 的值大于等于 4 时,将会返回满足我们条件的最后一个值
// 直到 count 的值再次小于或等于 3 为止。
const alwaysSmall = computed((previous) => {
  if (count.value <= 3) {
    return count.value
  }

  return previous
})
</script>

二 watch 差异较大!

2.1 基本语法

2.1.1 侦听一个

watch数据源可以是一个具有返回值的 getter 函数,也可以直接是一个 ref

    // 侦听一个 getter
    const state = reactive({ count: 0 })
    watch(
      () => state.count,
      (count, prevCount) => {
        /* ... */
      }
    )

    // 直接侦听一个 ref
    const count = ref(0)
    watch(count, (count, prevCount) => {
      /* ... */
    })

2.1.2 侦听多个

侦听多个的时候使用数组

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

    watch([firstName, lastName], (newValues, prevValues) => {
      console.log(newValues, prevValues)
    })

    firstName.value = 'John' // logs: ["John", ""] ["", ""]
    lastName.value = 'Smith' // logs: ["John", "Smith"] ["John", ""]
    
    
    !!!!! 多个同步更改只会触发一次
    const changeValues = () => {
        firstName.value = 'John'
        lastName.value = 'Smith'
        // 打印 ["John", "Smith"] ["", ""]
     }

注意点 如果你在同一个函数里同时改变这些被侦听的来源,watch仍只会执行一次

官方的解决方法(不推荐):
使用 async await 关键字,并且使用 nextTick 等待watch在下一步改变之前运行

    const changeValues = async () => {
      firstName.value = 'John' // 打印 ["John", ""] ["", ""]
      await nextTick()
      lastName.value = 'Smith' // 打印 ["John", "Smith"] ["John", ""]
    }

2.2 watch 特殊点 (主要是特殊的差异点为refreactive

2.2.1 当wacth监听的为ref数据

    const count = ref(0)
    watch(count, (count, prevCount) => {
      /* ... */
    })

问题
一. 为什么这里的 ref 监听的时候不需要.value呢?那么.vaule又是为什么

答:
    首先知道 我们通过 ref 声明的基本数据类型,会被改造成 refImpl 对象
    那么.value 相当于 拿到了这个基本数据类型 0
    但我们的wacth监听的根本不是这个基本数据类型,监听而是通过ref改造过后的 refImpl 对象

2.2.2 当wacth监听的为reactive数据

监听的数据为reactive声明的整个

    const info = reactive({
        name:'张三',
        age:18,
        obj:{
            a:{
                b:10
            }
        }
    })
    watch(info, (newValues, prevValues) => {
          console.log(newValues, prevValues)  // 打印 为同一个info 没有区分改变前和改变后的变化 ,均为改变后的值
    }, {deep:fales})  // deep 无效

总结 问题

  1. 无法正确的获取改变前的值,newValues, prevValues打印结果均为改变后的值
  2. deep为fales无效,相当于强制开启了deep,并且无法关闭。
  3. 解决方案 watch监听的数据为一个数组形式的 getter 函数

但是

当你监听info中的obj时

   watch(info.obj, (newValues, prevValues) => {
          console.log(newValues, prevValues)  
    }, {deep:true})  // deep 生效

总结 问题
1 当watch监听的数据为reactive中的一个对象属性时,deep是可以正常生效的 (疑问?为什么呢)