008.计算属性和侦听器

计算属性{computed}

插值模板内的表达式非常便利,但是其设计的初衷是用于 ➾ 简单运算。

  • 在插值模板中,放入太多的逻辑会让插值模板过重难以维护。

  • 🔥案例

    1
    2
    3
    <div id="box">
    {{ message.split('').reverse().join('') }}
    </div>
    • 🔔提示
      • 对于任何复杂逻辑,你都应当使用计算属性
      • ✨计算属性的作用
        • 防止插值模板过重难以维护。
  • 语法

    1
    2
    3
    <div id="box">
    {{ 函数名 }}
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    new Vue({
    el: '#box',
    computed: {
    函数名() {
    return 返回值
    }
    }
    })
  • 🔥案例

    1
    2
    3
    <div id="box">
    {{ myComputeName }}
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    new Vue({
    el: '#box',
    computed: {
    myComputeName() {
    return '计算属性'
    }
    }
    })

    image-20231207104656338

    • ❗注意

      • {{ myComputeName() }} ➾ ❎

        image-20231207104825928

方法和计算属性的异同

1.相同点

两种方式的最终结果完全相同。

2.不同点

  1. 方法{methods}

    • 每当触发重新渲染时,调用方法总会再次执行函数 ➾ methods方法没有缓存
  2. 计算属性{computed}

    • 只在相关依赖值发生改变时,才会重新计算,
    • 「多次访问」计算属性会立即返回之前的计算结果,而不必再次执行函数 ➾ computed计算属性有缓存
  • 🔥案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <div id="box">
    <!-- myMethodName➾ 方法 -->

    {{ myComputedName }}
    {{ myComputedName }}
    {{ myComputedName }}

    <!-- myMethodName➾ 计算属性 -->
    {{ myMethodName() }}
    {{ myMethodName() }}
    {{ myMethodName() }}
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    var vm = new Vue({
    el: "#box",
    data: {
    myname: "糖丸",
    },

    methods: {
    myMethodName() {
    console.log("myMethodName➾ 方法")

    return this.myname.substring(0, 1).toUpperCase() + this.myname.substring(1)
    }
    },

    computed: {
    myComputedName() {
    console.log("myMethodName➾ 计算属性")

    return this.myname.substring(0, 1).toUpperCase() + this.myname.substring(1)
    }
    }
    })
    gif-231207111300

🔥案例➾ 模糊查询{改造}

  • 计算属性 ➾ v-for="data in computedList"

    1
    2
    3
    4
    5
    6
    7
    8
    <div id="box">
    <input type="text" v-model="mytext" />
    <ul>
    <li v-for="data in computedList" :key="data">
    {{data}}
    </li>
    </ul>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var vm = new Vue({
    el: "#box",
    data: {
    mytext: "",
    datalist: ["aaa", "add", "bbb", "bbc", "ccc", "ddd", "eee", "ade"]
    },
    computed: {
    computedList() {
    return this.datalist.filter(item => item.includes(this.mytext))
    }
    }
    })

    gif-231123091931

🔥案例➾ 表单绑定➾ 购物车{计算金额改造}

  • 第三部分

    1
    2
    3
    4
      <div>总金额: {{ computedSum }}</div>

    {{checkList}}
    </div>
    • checkList ➾ 勾选的购物车数据数组。

    image-20231205094346834

    • computed()

      • computedSum() ➾ 累加计算 checkList 数组的每一项的价格×数量

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        computed: {
        computedSum() {
        var total = 0

        this.checkList.forEach(item => {
        total += item.price * item.number
        })

        return total
        }
        }
    gif-231205102111

侦听属性{watch}

Vue提供了一种更通用的方式来 ➾ 观察响应Vue实例上的数据变动 ➾ 侦听属性{watch}

  • 语法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    new Vue({
    el: '#box',
    data: {
    属性名: 属性值
    },
    watch: {
    属性名(属性值改变后的新值, 属性值改变之前的旧值) {
    ...
    }
    }
    })
  • 🔥案例 ➾ console.log("新值", nval, "旧值", oval)

    1
    2
    3
    4
    5
    6
    7
    8
    <div id="box">
    <input type="text" v-model="mytext" />
    <ul>
    <li v-for="data in datalist" :key="data">
    {{data}}
    </li>
    </ul>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var vm = new Vue({
    el: "#box",
    data: {
    mytext: "",
    datalist: ["aaa", "add", "bbb", "bbc", "ccc", "ddd", "eee", "ade"],
    originList: ["aaa", "add", "bbb", "bbc", "ccc", "ddd", "eee", "ade"]
    },

    watch: {
    mytext(nval, oval) {
    console.log("新值", nval, "旧值", oval)

    setTimeout(() => {
    this.datalist = this.originList.filter(item => item.includes(nval))

    }, 1000)
    }
    }
    })
    gif-231208105148

1.计算属性和侦听属性的区别

image-20231208102639087

  • 从上面流程图中,我们可以看出它们之间的区别 ➾ 
    1. 计算属性{computed}
      1. 监测的是「依赖值」➾
        1. 在依赖值不变时,计算属性直接读取 ➾ 缓存进行复用。
        2. 在依赖值变化时,计算属性才会重新计算。
      2. 支持缓存。
      3. 不支持异步。
    2. 侦听属性{watch}
      1. 监测的是「属性值」➾
        • 只要属性值发生变化,侦听属性都会执行 ➾ 侦听函数
      2. 不支持缓存。
      3. 支持异步。

2.深度侦听{deep}

侦听属性只能 ➾ 侦听对象数组本身的变化,而不能 ➾ 侦听内部嵌套的属性元素的变化。

  • 如果需要 ➾ 侦听对象数组 ➾ 内部嵌套的属性或元素的变化,则需要用到 ➾ deep属性。

  • 语法

    • deep选项被设置为true时,Vue会递归地遍历 ➾ 所有属性,监听它们的变化。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      new Vue({
      el: '#box',
      data: {
      属性名: 属性值
      },
      watch: {
      属性名: {
      handler(属性值改变「后」的新值, 属性值改变「前」的旧值) {
      ...
      },
      deep: true // 开启深度侦听
      }
      }
      })
      • 🔔提示
        • handler(nval, oval)
          1. 侦听属性中,侦听到数据变化后,需要具体执行的方法。
          2. 书写的侦听函数,其实默认书写的就是 ➾ handler函数。
  • 🔥案例

    1. 未开启深度侦听。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      var vm = new Vue({
      el: "#box",
      data: {
      datalist: {
      age: 20
      }
      },

      watch: {
      datalist: {
      handler(nval, oval) {
      console.log("新值", nval, "旧值", oval)
      }
      }
      }
      })

      gif-231208165250

    2. 已开启深度侦听。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      var vm = new Vue({
      el: "#box",
      data: {
      datalist: {
      age: 20
      }
      },

      watch: {
      datalist: {
      handler(nval, oval) {
      console.log("新值", nval.age, "旧值", oval.age)
      },
      deep: true
      }
      }
      })

      gif-231208164757

      • 单个属性的深度监听 ➾ datalist.age

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        var vm = new Vue({
        el: "#box",
        data: {
        datalist: {
        name: '糖丸',
        age: 20
        }
        },

        watch: {
        'datalist.age': {
        handler(nval, oval) {
        console.log("新值", nval, "旧值", oval)
        }
        }
        }
        })

3.立即调用{immediate}

  1. 当属性值「最初」绑定时,

    • 侦听属性不会执行 ➾ 侦听函数。
  2. 只有当属性值发生改变时,

    • 侦听属性才会执行 ➾ 侦听函数。
  3. 如果需要在「最初」绑定属性值时,让侦听属性执行 ➾ 侦听函数,则需要用到 ➾ immediate属性。

    • 语法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      new Vue({
      el: '#box',
      data: {
      属性名: 属性值
      },
      watch: {
      属性名: {
      handler(属性值改变后的新值, 属性值改变之前的旧值) {
      ...
      },
      immediate: true // 开启立即调用
      }
      }
      })
      • handler(nval, oval)
        1. 侦听属性中,侦听到数据变化后,需要具体执行的方法。
        2. 书写的侦听函数,其实默认书写的就是 ➾ handler函数。
    • 🔥案例

      • 如下代码代表 ➾ 在wacth中,声明myName后,立即执行handler()

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        var vm = new Vue({
        el: "#box",
        data: {
        myName: '糖丸'
        },

        watch: {
        myName: {
        handler(nval, oval) {
        console.log("新值", nval, "旧值", oval)
        this.myName = '糖宝'
        },
        immediate: true
        }
        }
        })

        image-20231208172051343

        • 🔔提示
          • 由于在页面加载时,就执行了 ➾ 
            • 一次侦听函数{此时属性值不存在},所以旧数据为undefined

计算属性的getter和setter函数

计算属性默认只有getter函数,也就是说,即使你没有明确写出getter函数,计算属性也会默认使用它。

  • 🔥案例

    1
    2
    3
    <div id="app">
    {{ fullName }}
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var vm = new Vue({
    el: '#app',
    data: {
    firstName: '糖',
    lastName: '丸'
    },
    computed: {
    fullName() {
    return this.firstName + this.lastName
    }
    }
    })

    image-20231212101304015

    • 明确写出getter函数。

      1
      2
      3
      4
      5
      6
      7
      computed: {
      fullName: {
      get() {
      return this.firstName + this.lastName
      }
      }
      }

setter函数getter函数类似,也是写在计算属性中。

  • 而不同之处在于,getter函数是默认用法,setter 函数不是默认用法 ➾ 

    • 如果你要使用 setter 函数,那么你必须要手动写出 setter 函数。
  • 🔥案例

    • 能显示名字的同时,还能改变名字姓与名之间要用中文逗号分隔。

      1
      2
      3
      <div id="app">
      {{ fullName }}
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      var vm = new Vue({
      el: '#app',
      data: {
      firstName: '糖',
      lastName: '丸'
      },
      computed: {
      fullName: {
      get() {
      return this.firstName + this.lastName
      },
      set(newName) { // 加上setter函数,可以传入新的名字
      var name = newName.split(',') // 把传入的名字根据逗号,拆分成数组

      this.firstName = name[0] // 数组的第一个元素为 firstName
      this.lastName = name[1] // 数组的第二个元素为 lastName
      }
      }
      }
      })

      gif-231212102508

      • 🔔提示
        • 当修改计算属性值时,就会触发setter函数,从而执行一些自定义的操作。
  • 🪄总结

    1. getter函数根据 ➾ getter函数中,变量的变化而变动。
    2. setter函数根据 ➾ 计算属性值的直接更改而变动。