Vue(二):计算属性与 watch 监听器

03. Vue 指令拓展

3.1 指令修饰符

可以通过 . 来指明一些指令的后缀,不同的后缀中封装了不同的操作,可以帮助我们简化代码,比如之前使用过的监听 enter 键的弹起,我们需要操作事件对象,来检测用户使用了哪个键,但是 Vue 替我们封装了直接检测回车的方式,@keyup.enter 就是在我们上面的 v-on 语句后面再加一个 . 修饰。

下面来给出具体的代码的示例

html 复制代码
  <div id="app">
    <h3>@keyup.enter  →  监听键盘回车事件</h3>
    <input @keyup.enter="fn"  v-model="username" type="text">
  </div>
html 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: ''
      },
      methods: {
        fn(e) {
          if (e.key === "Enter") {
            alert('回车');
          }
        }
      }
    })
  </script>

这样就是实现了搜索框监听回车的操作

其他常用的修饰符比如 v-model.trim 去除空格,也就是绑定完后获取到的内容是去除空格的,v-model.number 会尝试将获取的信息转换为数组,如果无法转化为数字的,比如我们输入 abc 就会保留原本的字符串的形式

html 复制代码
<div id="app">
    <h3>v-model修饰符 .trim .number</h3>
    姓名:<input v-model.trim="username" type="text"><br>
    年纪:<input v-model.number="age" type="text"><br>
</div>
html 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: '',
        age: '',
      },       
    })
  </script>

效果:

具体修改后的属性内容可以通过前面提到的 Vue 调试工具查看

另外的还有 @EVENT.stop 阻止事件冒泡和 @EVENT.prevent 阻止默认行为,比如表单的提交或者超链接的跳转行为

html 复制代码
    <!-- 阻止事件冒泡  -->
<h3>@事件名.stop     →  阻止冒泡</h3>
<div @click="fatherFn" class="father">
<div @click.stop="sonFn" class="son">儿子</div>
    <!-- 阻止默认行为  -->
<h3>@事件名.prevent  →  阻止默认行为</h3>
<a @click.prevent href="http://www.baidu.com">阻止默认行为</a>

3.2 v-bind 指令对于样式控制的增强

我们之前要控制 DOM 元素的样式是通过 ClassList.add( ) 等方式实现对 DOM 元素类的操控,那通过 Vue 我么可以怎么实现呢?

语法::class="对象/数组"

对象:通过布尔值来判断是否将这个类名加到盒子上

适用于一个类名来回切换的情况,比如 TAB 栏高亮

html 复制代码
<div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值}"></div>

数组:数组中的所有类都会加到盒子上,本质是一个 list 列表

适用于批量添加或者删除类的情况

html 复制代码
<div class="box" :class="[ '类名1', '类名2', '类名3']"></div>

来看一个实例

html 复制代码
<div id="app">
    <div class="box" :class="{pink: true, big: true}">黑马程序员</div>
    <div class="box" :class="['pink']">黑马程序员</div>
 </div>
html 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {

      }
    })
</script>

3.3 v-model 作用于其他的表单元素

常见的表单元素都可以通过 v-model 去关联,来快速的获取和设定表单元素的值,这里直接给出例子

html 复制代码
<!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>
  <style>
    textarea {
      display: block;
      width: 240px;
      height: 100px;
      margin: 10px 0;
    }
  </style>
</head>
<body>

  <div id="app">
    <h3>小黑学习网</h3>

    姓名:
      <input type="text" v-model="username"> 
      <br><br>

    是否单身:
      <input type="checkbox" v-model="isSingle"> 
      <br><br>

    <!-- 
      前置理解:
        1. name:  给单选框加上 name 属性 可以分组 → 同一组互相会互斥
        2. value: 给单选框加上 value 属性,用于提交给后台的数据
      结合 Vue 使用 → v-model
    -->
    性别: 
      <input type="radio" v-model="gender" value="男">男
      <input type="radio" v-model="gender" value="女">女
      <br><br>

    <!-- 
      前置理解:
        1. option 需要设置 value 值,提交给后台
        2. select 的 value 值,关联了选中的 option 的 value 值
      结合 Vue 使用 → v-model
    -->
    所在城市:
      <select v-model="location">
        <option>北京</option>
        <option>上海</option>
        <option>成都</option>
        <option>南京</option>
      </select>
      <br><br>

    自我描述:
      <textarea v-model="text"></textarea> 

    <button>立即注册</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: '',
        isSingle: true,
        gender: '男',
        location: '上海',
        text: ''
      }
    })
  </script>
</body>
</html>

需要注意的是 v-model 会为单选框加上 name 属性,将其绑定为一组

04. 计算属性

4.1 基本使用

基于现有的数据,计算出来的新的属性,依赖数据的 变化自动 重新计算

语法:声明在 computer 中,一个计算属性对应一个函数

使用起来和普通属性相同,比如插值表达式 {{ 计算属性 }}

html 复制代码
<div id="app">
    <h3>小黑的礼物清单</h3>
    <table>
      <tr>
        <th>名字</th>
        <th>数量</th>
      </tr>
      <tr v-for="(item, index) in list" :key="item.id">
        <td>{{ item.name }}</td>
        <td>{{ item.num }}个</td>
      </tr>
    </table>

    <!-- 目标:统计求和,求得礼物总数 -->
    <p>礼物总数:{{totalCount}} 个</p>
  </div>
html 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        // 现有的数据
        list: [
          { id: 1, name: '篮球', num: 1 },
          { id: 2, name: '玩具', num: 2 },
          { id: 3, name: '铅笔', num: 5 },
        ]
      },
      computed: {
        totalCount() {   
            // 求和
          return this.list.reduce((sum, item) => sum + item.num, 0);
        }
      }
    })
  </script>

4.2 计算属性 VS Method

computed 计算属性:封装了对一段数据的处理求得一个结果,其具有缓存特性,即对计算出来的结果进行缓存,再次使用的时候就直接读取缓存,当其依赖项变化的时候会重新计算并且读取缓存。

methods 方法:如果把计算逻辑写在 methods 中,每次调用都需要重新计算,可以想象对性能有怎样的损耗

html 复制代码
<div id="app">
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <h3>小黑的礼物清单🛒<span>{{ totalCountFn() }}</span></h3>
    <table>
      <tr>
        <th>名字</th>
        <th>数量</th>
      </tr>
      <tr v-for="(item, index) in list" :key="item.id">
        <td>{{ item.name }}</td>
        <td>{{ item.num }}个</td>
      </tr>
    </table>

    <p>礼物总数:{{ totalCountFn() }} 个</p>
  </div>
html 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        // 现有的数据
        list: [
          { id: 1, name: '篮球', num: 3 },
          { id: 2, name: '玩具', num: 2 },
          { id: 3, name: '铅笔', num: 5 },
        ]
      },

      methods: {
        totalCountFn () {
          console.log('methods方法执行了')
          let total = this.list.reduce((sum, item) => sum + item.num, 0)
          return total
        }
      },

      computed: {
        // 计算属性:有缓存的,一旦计算出来结果,就会立刻缓存
        // 下一次读取 → 直接读缓存就行 → 性能特别高
        // totalCount () {
        //   console.log('计算属性执行了')
        //   let total = this.list.reduce((sum, item) => sum + item.num, 0)
        //   return total
        // }
      }
    })
  </script>

上面每个 10 的计算都需要再次调用函数,所以对于这种情况应该使用 computed

4.3 完整写法

既然上面一直说这个 computed 里面的内容是属性,属性是可读写的,那计算属性是否是可读写的,应该如何写呢?

如果我们希望这个计算属性的改变会影响绑定的这一段数据,就需要自己编写逻辑去实现。

这时候就需要完整的配置对象了,其中的 get() 方法是我们读的操作,set 是写的操作,通过在内部去书写方法就可以实现对读写的配置。

html 复制代码
<div id="app">
    姓:<input type="text" v-model="firstName"> +
    名:<input type="text" v-model="lastName"> =
    <span>{{ fullName }}</span><br><br>
    
    <button @click="changeName">改名卡</button>
  </div>
html 复制代码
<script>
    const app = new Vue({
      el: '#app',
      data: {
        firstName: '刘',
        lastName: '备',
      },
      methods: {
        changeName () {
          this.fullName = '黄忠'
        }
      },
      computed: {
        fullName: {
          // (1) 当fullName计算属性,被获取求值时,执行get(有缓存,优先读缓存)
          //     会将返回值作为,求值的结果
          get () {
            return this.firstName + this.lastName
          },
          // (2) 当fullName计算属性,被修改赋值时,执行set
          //     修改的值,传递给set方法的形参
          set (value) {
            // console.log(value.slice(0, 1))          
            // console.log(value.slice(1))         
            this.firstName = value.slice(0, 1)
            this.lastName = value.slice(1)
          }
        }
      }
    })
  </script>

当我们进行修改的时候,等号后面的值会作为 value 传给 set 函数,上面实现了通过修改姓名来修改绑定的名和姓的操作。

05. watch 监听器

5.1 基本使用

watch 监听器的可以实现对数据变化的监听,比如网页翻译的场景,我们在左边的框中输入需要翻译的文字后没有做任何操作但是右边的翻译的内容改变了,这就是因为监听了我们的输入操作。

语法:

javascript 复制代码
const app = new Vue({
  data: {
    message: 'Hello'
  },
  watch: {
    // 监听 message 属性的变化
    message(newValue, oldValue) {
      console.log(`message 从 ${oldValue} 变为 ${newValue}`);
      // 在这里执行其他操作...
    }
  }
});

里面接收两个属性 newValueoldValue 来存储更改前和更改后的值

watch 中可以监听多个属性

javascript 复制代码
const app = new Vue({
  data: {
    firstName: 'John',
    lastName: 'Doe',
    fullName: ''
  },
  watch: {
    // 监听 firstName 和 lastName 两个属性的变化
    firstName(newFirstName, oldFirstName) {
      this.fullName = newFirstName + ' ' + this.lastName;
      // 在这里执行其他操作...
    },
    lastName(newLastName, oldLastName) {
      this.fullName = this.firstName + ' ' + newLastName;
      // 在这里执行其他操作...
    }
  }
});

5.2 完整写法

可以在watch选项中添加配置项,这些配置项包括handlerdeep

  • handler是一个函数,用于处理属性值变化时的逻辑。它接收两个参数,新值和旧值。
  • deep是一个布尔值,用于表示是否深度监听对象内部值的变化,默认为false

这里就需要写成配置对象,而不是上面的单个函数的形式,上面的函数写在对象的 handler() 函数中

javascript 复制代码
const app = new Vue({
  el: '#app',
  data: {
    message: 'Hello',
    count: 0,
    user: {
      name: 'John',
      age: 30
    }
  },
  watch: {
    message: {
      handler(newValue, oldValue) {
        console.log(`message 从 ${oldValue} 变为 ${newValue}`);
        // 在这里执行其他操作...
      },
      deep: true // 深度监听,如果message是对象,则也监听对象内部值的变化
    },
    count(newValue, oldValue) {
      console.log(`count 从 ${oldValue} 变为 ${newValue}`);
      // 在这里执行其他操作...
    },
    'user.name'(newValue, oldValue) { // 监听嵌套属性
      console.log(`用户姓名从 ${oldValue} 变为 ${newValue}`);
      // 在这里执行其他操作...
    }
  },
  methods: {
    increment() {
      this.count++;
    },
    changeUserName() {
      this.user.name = 'Alice'; // 改变嵌套属性,触发监听
    }
  }
});
相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax