没时间学 Vue (13) —— 绑定(五):计算属性和侦听器

上一篇《没时间学 Vue (12) —— 组件(二):组件的创建、使用和数据传递》中,我们遇到了 “冻龄” 的问题。

本篇,我们来拆解一下这个问题,并且引入可能的解决方案 —— 计算属性和侦听器。

Vue 官方的介绍在:https://cn.vuejs.org/v2/guide/computed.html

 

1、问题的原因


我们在年龄组件 Age.vue 中,是这样计算和显示年龄的 。

貌似年龄只在初始时计算了一次,之后无论生日的数据如何变化都没有更新。

问题有了,先不要着急找解决方案,先来验证一下是不是我们所推测的 “问题” —— 万一不是,那就白忙活了。

我们要怎么验证呢?其实蕴含在我们的推测中,需要把推测按照程序的方式拆解出来。

1)computeAge() 函数只被调用了一次;

2)App.vue 中生日选择控件的数据变更时,年龄组件中的生日数据也会跟着变化。

也就是说,我们只要:

1)增加一个数据,来记录 computeAge() 函数调用次数;

2)把 computeAge() 函数和当前的生日数据显示出来

就可以进行验证了。(等我们之后讲完 “调试” 之后,可以使用更简单的方法进行验证了)

验证的代码大概是这样的:

而验证结果是这样的:(我们的猜测完全正确)

找对问题、找到原因就好办了,接下来我们来解决问题。


2、方法一:侦听器


最容易想到也是最直观的办法是,让年龄跟着生日的变化重新计算 —— 听起来貌似一句废话,这个本来就是我们想要做的效果。

但是这句人能听懂的废话只有拆解成程序能听懂的 “代码”,才能发挥作用。程序想听的大概是这样的:

1)监听 “生日” 的变更事件;

2)“生日” 变更事件里,重新计算 “年龄” 。

为了实现上面的步骤 1),我们需要用到 Vue 提供的 “侦听器”。

侦听器的写法比较特别,需要在 export default 中加入这么一段代码:

  watch: {
    birthday: function() {
      this.age = this.computeAge();
    }
  }

也就是:

之后,年龄就能跟着生日自动变化了。

上面的代码中,我们只是用到了最基本(也最常用)的侦听处理方式。

比较完整的侦听处理方式的说明在 https://cn.vuejs.org/v2/api/#watch

而且我们写的侦听处理实际上会被转换成 vm.$watch() 的函数调用( https://cn.vuejs.org/v2/api/#vm-watch)。

完整的处理方式功能强大也比较复杂,先有个印象就行 —— 最好还能记住,监听处理函数是可以带参数的(能知道变更前、变更后的值)

birthday: function(newValue, oldValue) {

前面那个参数是变更后的值,后面那个参数是变更前的值。

不过要是不用这两个参数的话,就不要写了 —— 不然静态检查的时候会报错,说你画蛇添足、定义了变量却不使用。

 

3、方法二:计算属性


1)计算属性 —— “计算” 和 “属性” —— 是什么?

先抛开 “计算属性” 这四个字,我们来挖一下使用的上面提到的 “侦听器” 的时候,代码实际上大概是怎么运行的。

1)我们定义了 2 个变量,birthday (生日) 和 age (年龄);

2)当 birthday 发生变化 —— 也就是在设置 birthday 的时候 —— 更新 age 的值。

 写成 Java 的话,大概是这样的:(由于没有 “属性” 这个概念,所以我们实际上是折腾的 get/set 函数)

class AgeComponent {
    private String birthday; // 生日(可读可写)
    private String age; // 年龄(只读)

    public String getBirthday() {
        return this.birthday;
    }

    public void setBirthday(String newBirthday) {
        this.birthday = newBirthday;
        // 更新 this.age
        this.age = this.computeAge();
    }

    public String getAge() {
        return this.age;
    }
}

要是写成 C# 的话,大概是这样的 —— 没错,C# 是支持 “属性” 的 —— 不过实际上也是 get/set 函数的语法糖。

class AgeComponent {
    private String birthday; // “生日” 的成员变量,外部不能直接访问
    public String Birthday { // “生日” 的属性
        get {
            return this.birthday;
        }

        set {
            this.birthday = value;
            this.age = this.computeAge();
        }
    }

    private String age; // “年龄” 的成员变量
    public String Age { // “年龄” 的属性
        get {
            return this.age;
        }
    }
}

哦,不好也就这样,看上去好像没有什么特别的啊?是的 —— 如果上面的场景还比较隐晦的话,我们来看下面这个。

假设现在要做一个跟圆相关的控件,接收的参数是半径,需要输出周长和面积,这个时候会怎么做呢?

Java 地话大概会有以下两种写法:(估计你会比较本能地想到写法二,而不会选择写法一)

换成 C# 的话,则能更直观地看出 “计算” 和 “属性” 是什么意思。

如上图所示,计算属性的特点是:

1)该属性没有对应的成员变量;

2)该属性的值是计算出来的 —— 因此,也不需要监听其他的属性的变更事件、在变更时更新本属性的值。

 

2)计算属性的写法:

上面拿 Java 和 C# 铺垫了半天,现在终于可以回到 Vue —— 准确的说是 JavaScript —— 上来了。

JavaScript 本身也是支持属性的,用法跟 C# 比较像,只要提供 get 和 set 函数就行了。

比如说,用 JavaScript 来写圆的面积属性的话,大概是这样的:(get/set 的更多内容可以参看:https://zh.javascript.info/property-accessors。)

其实不光 JavaScript 和 C#,Php 和 Python 也支持类似的 get/set 的方式来 “定制” 属性 —— 的读写操作。

再扯远一点儿,虽然 JavaScript 和 Java 的名字很像,语法却大相径庭、完全不是一个妈生的;

如果 C# 学得不错的话,可以用微软出的 TypeScript,跟 C# 比较像,而且可以编译成 JavaScript 。具体可以参照:https://www.tslang.cn/

应用到 Vue 中的话,要这么来写:

  data: function() {
    return {
      callTimes: 1, // 计算次数
      // age: this.computeAge() // 从 data 中挪到 computed 中
    }
  },
  computed: {
    age: {
      get: function() {
        return this.computeAge();
      }
    }
  },

关键的修改如下:(注意结合 JavaScript 的 get/set 属性操作来看)

虽然 JavaScript 的属性支持 get / set 读写两种函数,但是 Vue 计算属性中往往咱们只会指定 get 函数(比如说:会通过半径来计算周长,但是很少通过周长来计算半径)。

此时,计算属性的写法还可以再简化一下,又能少敲几个字符 —— 主要是嵌套层级能少一层,可读性能更好一些。

Vue 计算属性比较完整的资料,可以参照:https://cn.vuejs.org/v2/api/#computed

当然,还是推荐借此机会也好好看一下 JavaScript 的 get/set:https://zh.javascript.info/property-accessors

 

3)计算属性是怎么自动计算的呢?

在惊叹计算属性的简单便利之余,你可能会好奇地问:计算属性是怎么更新的呢?

为什么 birthday 变了之后,age 就会跟着变了呢?谁来自动地调用计算属性的计算操作的呢?

恭喜你,又思考了一个超越 80% 同行的问题。

这个问题很复杂,简单理解的话,就是 Vue 根据 computed 定义、自动创建了一堆侦听器 (vm.$watch) 。

想知道更多的机制的话,就自己搜索吧。https://segmentfault.com/a/1190000016368913 这篇也不错。

 

4、方法三:纯函数


你可能还会有另一种 “灵感”,就是直接把模板中的 {{age}} 替换成 {{computeAge()}},什么侦听器啊、计算属性啊全部干掉,简单粗暴。

画面效果来看,也实现了年龄跟着生日变化的要求。

但是 —— 敲黑板了哈 —— 注意看上图中的 “计算次数”!

光是初始就要计算 100 多次,生日变更时还要再计算 100 多次,效率是有问题的!

(为什么会调用这么多次呢?这个得深入巴拉 Vue 的底层机制才知道,现在只需要记住这点就行了:不要直接使用函数替代计算属性或者侦听器,没有缓存,要调用很多次,数量有问题

Vue 官网也专门准备了两个章节,简单做了说明:

Vue 的官网首推计算属性、反对滥用侦听器 —— 主要是从代码的可读性和可维护的角度来考虑的。

实际项目中则两者都会用到,比如说:会监听路由,或者会在监听处理函数中异步做一些处理。

 

5、思考题


本章的思考题如下:

1)实现计算圆的周长和面积的控件,并在 App.vue 中使用和测试

接收参数:半径,

输出结果:周长

2)实现一个验证码生成器的控件,并在 App.vue 中使用和测试

接收参数:验证码长度 (默认长度为 4 ;验证码长度变化时,需要重新生成验证码)

输出结果:验证码(指定长度的随机生成的数字,需要用到 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/random

控件自带 “重新生成” 按钮,点击后重新生成验证码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值