VUE|侦听器

发布时间 2023-07-21 11:39:09作者: Weltㅤ

1 侦听器

1) 什么是侦听器

可以通过watch配置项, 监听已经存在的属性的改变

2) 语法

watch: {
  // 监听data中的firstName属性
  firstName() {
   // 执行一系列的操作
  },
},watch: {
  // 监听data中的firstName属性
  firstName() {
   // 执行一系列的操作
  },
},

示例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      姓: <input type="text" v-model="lastName" /> <br />
      名: <input type="text" v-model="firstName" /> <br />
      全名(侦听器实现): {{fullName}}
    </div>

    <script>
      const { createApp } = Vue

      const vm = createApp({
        data() {
          return {
            lastName: '',
            firstName: '',
            fullName: '',
          }
        },
        watch: {
          // 侦听lastName的变化, 当lastName变化时, 执行该函数
          lastName() {
            this.fullName = this.lastName + this.firstName
          },
          // 侦听firstName的变化, 当firstName变化时, 执行该函数
          firstName() {
            this.fullName = this.lastName + this.firstName
          },
        },
      }).mount('#app')
    </script>
  </body>
</html>

3) 特点

在watch对应的回调函数中, 可以获取到新值旧值

示例

const vm = new Vue({
  el: '#app',
  data: {
    firstName: '',
    lastName: '',
  },
  // 使用watch这个配置项
  watch: {
    // 在watch对应的回调函数中, 可以得到新值和旧值
    // 对于简单数据类型, 可以获取新旧值
    // 对于引用数据类型, 不能获取旧值
    firstName(newValue, oldValue) {
      // 一对多: 监听某一个属性的改变, 做一系列的操作
      console.log('firstName改变了...')
      console.log('新的值:', newValue)
      console.log('旧的值:', oldValue)
    },
  },
})

侦听器的高级用法

  • immediate: true => 立即执行watch的回调
  • flush: 'post' => 让watch的回调在DOM更新之后执行
  • 对watch值类型属性, 可以在watch的回调中获取到新旧值

2 计算属性 VS 侦听器

  1. 是否会在vm实例中挂载新属性?

    1. computed会
    2. watch不会
  2. 对应关系

    1. computed是多对一, 可以同时监听多个值改变, 最终计算得到一个新的属性
    2. watch是一对多, 主要监听一个属性的变化, 执行多种逻辑
  3. 能否获取新旧值?

    1. computed不能
    2. watch能

3 综合作业

需求

实现如下购物车效果

  1. 数量不能<1
  2. 修改数量, 总价格会实时计算
  3. 当所有的商品全部移除时, 显示购物车中没有商品
购物车.gif
<template>
  <div class="about">
    <table cellspacing="0">
      <caption>
        <h1>购物车</h1>
        <br />
      </caption>
      <tr>
        <th>序号</th>
        <th>书籍名称</th>
        <th>出版日期</th>
        <th>价格</th>
        <th>购买数量</th>
        <th>操作</th>
      </tr>

      <tr v-for="(book, index) in books" :key="book.id">
        <td>{{ book.id }}</td>
        <td>{{ book.name }}</td>
        <td>{{ book.time }}</td>
        <td>{{ book.cost }}</td>
        <td>
          <el-button size="small" @click="minusNum(index)"
            ><el-icon><Minus /></el-icon
          ></el-button>
          <a>{{ book.num }}</a>
          <el-button size="small" @click="addNum(index)"
            ><el-icon><Plus /></el-icon
          ></el-button>
        </td>
        <td>
          <el-button size="small" @click="clickClear(index)">移除</el-button>
        </td>
      </tr>
    </table>
    <div class="summary">汇总金额:{{ total }}元</div>
  </div>
</template>

<script setup>
import { ElMessage } from 'element-plus'
import { computed, ref } from 'vue'
const books = ref([
  {
    id: 1,
    name: '《密室收藏家》',
    time: '2020.02.09',
    cost: '30RMB',
    num: '2',
  },
  {
    id: 2,
    name: '《东方快车谋杀案》',
    time: '2021.12.09',
    cost: '40RMB',
    num: '7',
  },
  {
    id: 3,
    name: '《尼罗河上的惨案》',
    time: '2022.10.06',
    cost: '50RMB',
    num: '5',
  },
  {
    id: 4,
    name: '《罗杰疑案》',
    time: '2021.10.06',
    cost: '45RMB',
    num: '8',
  },
])
function clickClear(index) {
  this.books.splice(index, 1)
}
function addNum(index) {
  this.books[index].num++
}
function minusNum(index) {
  if (this.books[index].num <= 0) {
    this.books[index].num = 0
    ElMessage({
      message: '书本数量不能为负数!',
      type: 'warning',
    })
  } else {
    this.books[index].num--
  }
}
const total = computed(() => {
  let sum_money = 0
  books.value.forEach((element) => {
    let money = parseFloat(element.cost)
    let number = parseFloat(element.num)
    sum_money += money * number
  })
  return sum_money
})
</script>

<style>
table tbody {
  display: block;
  height: 390px;
  width: 100%;
  overflow-y: scroll;
}

table {
  width: 100%;
}

tr {
  border-top: 1px solid #c1c3d1;
  border-bottom: 1px solid #c1c3d1;
  color: #666b85;
  font-size: 15px;
  font-weight: normal;
  text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
  width: 100%;
}

tr:hover td {
  background: #4e5066;
  color: #ffffff;
  border-top: 1px solid #22262e;
}

tr:first-child {
  border-top: none;
}

tr:last-child {
  border-bottom: none;
}

tr:nth-child(odd) td {
  background: #ebebeb;
}

tr:nth-child(odd):hover td {
  background: #4e5066;
}

tr:last-child td:first-child {
  border-bottom-left-radius: 3px;
}

tr:last-child td:last-child {
  border-bottom-right-radius: 3px;
}

td {
  width: 300px;
  background: #ffffff;
  padding: 5px;
  text-align: center;
  vertical-align: middle;
  font-weight: 300;
  font-size: 15px;
  text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.1);
  border-right: 1px solid #c1c3d1;
  white-space: nowrap;
}

td a {
  margin-left: 5px;
  margin-right: 5px;
  font-weight: 300;
  width: 20px;
  display: inline-block;
}

td:last-child {
  border-right: 0px;
}

.summary {
  margin-top: 10px;
  text-align: right;
  color: #666b85;
  font-size: 15px;
  font-weight: bold;
  text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
}
</style>