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'; // 改变嵌套属性,触发监听
    }
  }
});
相关推荐
前端大卫17 分钟前
【重磅福利】学生认证可免费领取 Gemini 3 Pro 一年
前端·人工智能
孜燃28 分钟前
Flutter APP跳转Flutter APP 携带参数
前端·flutter
脾气有点小暴40 分钟前
前端页面跳转的核心区别与实战指南
开发语言·前端·javascript
lxh01131 小时前
最长递增子序列
前端·数据结构·算法
vipbic1 小时前
我封装了一个“瑞士军刀”级插件,并顺手搞定了自动化部署
vue.js·nuxt.js
Youyzq1 小时前
前端项目发布到cdn上css被编译失效问题rgba失效和rgb失效
前端·css·算法·cdn
San30.2 小时前
深入 JavaScript 内存机制:从栈与堆到闭包的底层原理
开发语言·javascript·udp
Fantastic_sj2 小时前
Vue3相比Vue2的改进之处
前端·javascript·vue.js
vipbic2 小时前
解决npm publish的404/403和配置警告全记录
前端·npm·node.js
Bigger3 小时前
🚀 “踩坑日记”:shadcn + Vite 在 Monorepo 中配置报错
前端·react.js·vite