vue2升级vue3之computed与watch

183 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

vue2

在vue2中计算属性(computed)和侦听器(watch)是可以直接使用的(选项式API)

计算属性和侦听器 — Vue.js 中文文档

使用计算属性:

 computed: {
     // 计算属性的 getter
     reversedMessage: function () {
       // `this` 指向 vm 实例
       return this.message.split('').reverse().join('')
     }
 }

使用侦听器:

 watch: {
     firstName: function (val) {
       this.fullName = val + ' ' + this.lastName
     },
     lastName: function (val) {
       this.fullName = this.firstName + ' ' + val
     }
 }

而在vue3的组合式api中,二者需要我们自己手动引入再使用。

vue3计算属性computed

使用计算属性来描述依赖响应式状态的复杂逻辑。

实例

computed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 publishedBooksMessage.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

 <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>
 ​
 <template>
   <p>Has published books:</p>
   <span>{{ publishedBooksMessage }}</span>
 </template>

若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存

可写计算属性

计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到“可写”的属性,你可以通过同时提供 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>

现在当你再运行 fullName.value = 'John Doe' 时,setter 会被调用而 firstNamelastName 会随之更新。

这样原本我们只能通过改变firstName和lastName来更新fullName,我们使用了set后,可以给计算属性赋值,从而修改依赖项

vue3侦听器watch

计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。

实例

 <script setup>
 import { ref, watch } from 'vue'
 ​
 const question = ref('')
 const answer = ref('Questions usually contain a question mark. ;-)')
 ​
 // 可以直接侦听一个 ref
 watch(question, async (newQuestion, oldQuestion) => {
   if (newQuestion.indexOf('?') > -1) {
     answer.value = 'Thinking...'
     try {
       const res = await fetch('https://yesno.wtf/api')
       answer.value = (await res.json()).answer
     } catch (error) {
       answer.value = 'Error! Could not reach the API. ' + error
     }
   }
 })
 </script>
 ​
 <template>
   <p>
     Ask a yes/no question:
     <input v-model="question" />
   </p>
   <p>{{ answer }}</p>
 </template>
 ​

watch 的第一个参数可以是不同形式的“数据源”:它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组:

 const x = ref(0)
 const y = ref(0)
 ​
 // 单个 ref
 watch(x, (newX) => {
   console.log(`x is ${newX}`)
 })
 ​
 // getter 函数
 watch(
   () => x.value + y.value,
   (sum) => {
     console.log(`sum of x + y is: ${sum}`)
   }
 )
 ​
 // 多个来源组成的数组
 watch([x, () => y.value], ([newX, newY]) => {
   console.log(`x is ${newX} and y is ${newY}`)
 })