当前位置:   article > 正文

Vue3中的computed和watch属性

Vue3中的computed和watch属性


1. computed计算属性

简写写法(只实现了 get ):

<template>
  <div>
    <ul>
      <li v-for="item of carts" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
    <hr />
    <h3>合计:{{ totalPrice }}</h3>
  </div>
</template>

<script>
import { computed, reactive, toRefs } from 'vue'
export default {
  setup() {
    let state = reactive({
      carts: [
        { id: 1, name: '小米', price: 1, num: 1 },
        { id: 2, name: '大米', price: 1, num: 1 }
      ],
      total: 0
    })

    // 计算属性   -- get
    let totalPrice = computed(() => {
      return state.carts.reduce((p, { price, num }) => {
        p += price * num
        return p
      }, 0)
    })

    // let totalPrice = computed({
    //   get() {
    //     return state.carts.reduce((p, { price, num }) => {
    //       p += price * num
    //       return p
    //     }, 0)
    //   }
    // })

    return { ...toRefs(state), totalPrice }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

在这里插入图片描述

标准写法(可以实现 get 和 set 方法):

<template>
  <div>
    <ul>
      <li v-for="item of carts" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
    <hr />
    <h3>合计:{{ totalPrice }}</h3>
  </div>
</template>

<script>
import { computed, reactive, toRefs } from 'vue'
export default {
  setup() {
    let state = reactive({
      carts: [
        { id: 1, name: '小米', price: 1, num: 1 },
        { id: 2, name: '大米', price: 1, num: 1 }
      ],
      total: 0
    })

    let totalPrice = computed({
      get() {
        return state.carts.reduce((p, { price, num }) => {
          p += price * num
          return p
        }, 0)
      },
      // 了解一下
      set(v) {
        console.log('set', v)
      }
    })
    // 如果你给计算属性赋值,则一定要写标准方式,set方法一定要写
    totalPrice.value = 'abc'
    return { ...toRefs(state), totalPrice }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

在这里插入图片描述

完整的购物车案例:

<template>
  <div>
    <h3>{{ total }}</h3>
    <ul>
      <li v-for="(item, index) of carts" :key="item.id">
        <span>{{ item.name }}</span>
        <span>
          <button @click="setNum(index, 1)">+</button>
          <button @click="totalPrice = { index, n: 1 }">另一种实现求和的方式</button>
          <span>{{ item.num }}</span>
          <button @click="setNum(index, -1)">-</button>
        </span>
      </li>
    </ul>
    <hr />
    <h3>合计:{{ totalPrice }}</h3>
  </div>
</template>

<script>
import { computed, reactive, toRefs } from 'vue'
export default {
  setup() {
    let state = reactive({
      carts: [
        { id: 1, name: '小米', price: 1, num: 1 },
        { id: 2, name: '大米', price: 1, num: 1 }
      ],
      total: 0
    })

    let totalPrice = computed({
      get() {
        return state.carts.reduce((p, { price, num }) => {
          p += price * num
          return p
        }, 0)
      },
      // 求和方式2:
      set(v) {
        if (v.n) {
          state.carts[v.index].num += v.n
        }
      }
    })

    // 求和方式1:
    const setNum = (index, n) => {
      state.carts[index].num += n
      if (state.carts[index].num <= 1) state.carts[index].num = 1
      if (state.carts[index].num >= 10) state.carts[index].num = 10
    }

    return { ...toRefs(state), totalPrice, setNum }
  }
}
</script>

<style lang="scss" scoped></style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

在这里插入图片描述

2. watch侦听器属性

  • watchEffect 是自动去根据方法中的调用来确定依赖项。
  • watch 是手动指定依赖项 – 用它可能会多一些。

2.1 watchEffect

<template>
  <div>
    <h3>{{ num }}</h3>
    <!-- 输入的内容发生变化时,控制台不会打印,因为 name 不是侦听器的依赖项 -->
    <input type="text" v-model="name" />
    <button @click="num++">+++</button>
    <!-- <button @click="stop">停止侦听</button> -->
  </div>
</template>

<script>
// watchEffect 自动去根据方法中的调用来确定依赖项
// watch 手动指定依赖项  -- 用它可能会多一些
import { ref, watch, watchEffect } from 'vue'
export default {
  setup() {
    const num = ref(100)

    const name = ref('')

    // 侦听,它必须要有依赖项
    // 它在初始化时,会主动执行1次,如果没有依赖项,则不会再次执行
    // watchEffect(() => {
    //   // 这里只会在初始时打印一次222
    //   console.log(222)
    // })

    watchEffect(() => {
      // 此时它的依赖项为 num变量,如果它有改变,则回调函数会自动触发
      // 它的依赖项可以是1到N个
      console.log(222, num.value)
    })

    return { num,name }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

在这里插入图片描述

watchEffect 的返回值:

<template>
  <div>
    <h3>{{ num }}</h3>
    <button @click="num++">+++</button>
    <button @click="stop">停止侦听</button>
  </div>
</template>

<script>
// watchEffect 自动去根据方法中的调用来确定依赖项
// watch 手动指定依赖项  -- 用它可能会多一些
import { ref, watch, watchEffect } from 'vue'
export default {
  setup() {
    const num = ref(100)

    const stopHandle = watchEffect(() => {
      console.log(222, num.value)
    })

    const stop = () => stopHandle()

    return { num, stop }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在这里插入图片描述

watchEffect 的 onCleanup 回调参数:

watchEffect 还有一个回调参数,此参数它也是一个函数,作用是清理副作用。

<template>
  <div>
    <h3>{{ num }}</h3>
    <button @click="num++">+++</button>
    <button @click="stop">停止侦听</button>
  </div>
</template>

<script>
// watchEffect 自动去根据方法中的调用来确定依赖项
// watch 手动指定依赖项  -- 用它可能会多一些
import { ref, watch, watchEffect } from 'vue'
export default {
  setup() {
    const num = ref(100)
    let timer

    // 此方法它还有一个回调参数,此参数它也是一个函数,作用,清理副作用
    const stopHandle = watchEffect(
      onCleanup => {
        console.log(num.value)
        // console.log(222, num.value, name.value)
        onCleanup(() => {
          console.log('清理上一次的处理')
          timer && clearTimeout(timer)
        })
        timer = setTimeout(() => {
          console.log('输出的')
        }, 1000)
      },
      {
        // flush?: 'pre' | 'post' | 'sync';
        // pre 模板渲染之前触发
        // post 模板渲染之后触发
        // sync 和模板渲染同步来触发
        // 默认为 pres
        flush: 'pre'
      }
    )

    const stop = () => stopHandle()

    return { num, stop }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

在这里插入图片描述

2.2 watch

监听一个变量值的变化:

<template>
  <div>
    <div>{{ num }} <button @click="num++">+++</button></div>
    <input type="text" v-model="name" />
  </div>
</template>

<script>

import { ref, watch } from 'vue'
export default {
  setup() {
    const num = ref(100)
    const name = ref('')

    // 监听num变量它的变化,如果有变化,则触发
    watch(
      num,
      (newValue, oldValue) => {
        console.log(newValue)
      },
      {
        // 初始化执行1次,默认为false
        immediate: false
      }
    )

    return { num, name }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在这里插入图片描述

监听多个变量值的变化:

<template>
  <div>
    <div>{{ num }} <button @click="num++">+++</button></div>
    <input type="text" v-model="name" />
  </div>
</template>

<script>
import { ref, watch } from 'vue'
export default {
  setup() {
    const num = ref(100)
    const name = ref('')

    // 以数组的方式来写,它可以监听多个变量的值的变化,回调参数为一个数组
    watch([num, name], (nArr, oArr) => {
      console.log(nArr)
    })
    // 在工作中,通常只关心新的值,所以只会写一个参数,就像下面这样
    // watch([num, name], ([numValue, nameValue]) => {
    //   console.log(numValue, nameValue)
    // })

    return { num, name }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

在这里插入图片描述

监听对象中具体的值:

<template>
  <div>
    <input type="text" v-model="user.name" />
  </div>
</template>

<script>
import { ref, watch } from 'vue'
export default {
  setup() {
    const user = ref({ id: 1, name: '张三' })

    // 目前对于ref定义的引用类型,默认无法监听
    // watch(user, (n, o) => {
    //   console.log(n)
    // })

    // 但是可以指定监听对象中具体的值
    watch(
      // 'obj.name'(n,o){}
      () => user.value.name,
      (n, o) => {
        console.log(n)
      }
    )

    return { user }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

在这里插入图片描述

监听ref的引用对象(添加参数3):

<template>
  <div>
    <input type="text" v-model="user.name" />
  </div>
</template>

<script>
import { ref, watch } from 'vue'
export default {
  setup() {
    const user = ref({ id: 1, name: '张三' })

    // 监听ref的引用对象 添加参数3
    watch(user, (n, o) => {
      console.log(n)
    },{
      deep:true
    })

    return { user }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

在这里插入图片描述

监听 reactive 定义的响应变量:

<template>
  <div>
    <input type="text" v-model="user.name" />
  </div>
</template>

<script>
import { reactive, watch } from 'vue'
export default {
  setup() {
    const user = reactive({ id: 1, name: '张三' })

    // 监听的是一个reactive定义的响应变量,它默认就可以进行深层监听
    // 所以在工作中,如果你定义的值它是一个引用类型,建议使用reactive,基本类型用ref
    const stop = watch(user, (n, o) => {
      console.log(n)
    })

    return { user, stop }
  }
}
</script>

<style lang="scss" scoped>

</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/318655
推荐阅读