computed -计算属性

被计算出来的属性就是计算属性

  • 类型{ [key: string]: Function | { get: Function, set: Function } }

下面这段代码想要显示变量 message 的翻转字符串,但在模板中放入太多的逻辑让模板过重且难以维护。

  1. <div id="example">
  2. {{ message.split('').reverse().join('') }}
  3. </div>

基础例子

我们声明了一个计算属性 reversedMessage。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数:

  1. <div id="example">
  2. <p>Original message: "{{ message }}"</p>
  3. <p>Computed reversed message: "{{ reversedMessage }}"</p>
  4. </div>
  1. var vm = new Vue({
  2. el: '#example',
  3. data: {
  4. message: 'Hello'
  5. },
  6. computed: {
  7. // 计算属性的 getter
  8. reversedMessage: function () {
  9. // `this` 指向 vm 实例
  10. return this.message.split('').reverse().join('')
  11. }
  12. }
  13. })

结果:
Original message: “Hello”
Computed reversed message: “olleH”

可以打开浏览器的控制台,自行修改例子中的 vm。vm.reversedMessage 的值始终取决于 vm.message 的值。

  1. console.log(vm.reversedMessage) // => 'olleH'
  2. vm.message = 'Goodbye'
  3. console.log(vm.reversedMessage) // => 'eybdooG'

你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。

计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。

计算属性方法

可以通过在表达式中调用方法来达到同样的效果:

  1. <p>Reversed message: "{{ reversedMessage() }}"</p>
  1. // 在组件中
  2. methods: {
  3. reversedMessage: function () {
  4. return this.message.split('').reverse().join('')
  5. }
  6. }

将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新计算。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

计算属性缓存

计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
注意如果你为一个计算属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。

  1. computed: {
  2. aDouble: vm => vm.a * 2
  3. }

计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。

  • 示例

    1. var vm = new Vue({
    2. data: { a: 1 },
    3. computed: {
    4. // 仅读取
    5. aDouble: function () {
    6. return this.a * 2
    7. },
    8. // 读取和设置
    9. aPlus: {
    10. get: function () {
    11. return this.a + 1
    12. },
    13. set: function (v) {
    14. this.a = v - 1
    15. }
    16. }
    17. }
    18. })
    19. vm.aPlus // => 2
    20. vm.aPlus = 3
    21. vm.a // => 2
    22. vm.aDouble // => 4
  • 参考计算属性

注意,如果某个依赖 (比如非响应式 property) 在该实例范畴之外,则计算属性是不会被更新的。这意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

  1. computed: {
  2. now: function () {
  3. return Date.now()
  4. }
  5. }

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

为什么需要缓存?

假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

如果依赖的属性没有变化,就不会重新计算
getter/setter默认不会做缓存,Vue做了特殊处理

watch

监听/侦听

**
Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

watch有两种语法,他们是等价的

语法1

  • 类型{ [key: string]: string | Function | Object | Array }
  • 详细
    一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个 property。

    1. watch:{
    2. a: function (value, oldValue) { },
    3. b() { }, //省略了:function
    4. c: [f1, f2],
    5. d: 'methodName',
    6. e: { handler: fn, deep: true, immediate: true },
    7. 'object.a': function () { },
    8. },

    **

  • 示例

    1. var vm = new Vue({
    2. data: {
    3. a: 1,
    4. b: 2,
    5. c: 3,
    6. d: 4,
    7. e: {
    8. f: {
    9. g: 5
    10. }
    11. }
    12. },
    13. watch: {
    14. a: function (val, oldVal) {
    15. console.log('new: %s, old: %s', val, oldVal)
    16. },
    17. // 方法名
    18. b: 'someMethod',
    19. // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
    20. c: {
    21. handler: function (val, oldVal) { /* ... */ },
    22. deep: true
    23. },
    24. // 该回调将会在侦听开始之后被立即调用
    25. d: {
    26. handler: 'someMethod',
    27. immediate: true
    28. },
    29. // 你可以传入回调数组,它们会被逐一调用
    30. e: [
    31. 'handle1',
    32. function handle2 (val, oldVal) { /* ... */ },
    33. {
    34. handler: function handle3 (val, oldVal) { /* ... */ },
    35. /* ... */
    36. }
    37. ],
    38. // watch vm.e.f's value: {g: 5}
    39. 'e.f': function (val, oldVal) { /* ... */ }
    40. }
    41. })
    42. vm.a = 2 // => new: 2, old: 1

注意,不应该使用箭头函数来定义 watcher 函数,例如:

  1. searchQuery: newValue =>this.updateAutocomplete(newValue)

因为,箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.updateAutocomplete 将是 undefined。

语法2-vm.$watch( expOrFn, callback, [options] )

  • 参数
    • {string | Function} expOrFn
    • {Function | Object} callback
    • {Object} [options]
      • {boolean} deep
      • {boolean} immediate
  • 返回值{Function} unwatch
  • 用法
    观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数得到的参数为新值和旧值。表达式只接受简单的键路径。对于更复杂的表达式,用一个函数取代。
    注意:在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。
  1. vm.$watch('xxx', fn, { deep:..., immediate:...})
  • 示例

    1. // 键路径
    2. vm.$watch('a.b.c', function (newVal, oldVal) {
    3. // 做点什么
    4. })
    5. // 函数
    6. vm.$watch(
    7. function () {
    8. // 表达式 `this.a + this.b` 每次得出一个不同的结果时
    9. // 处理函数都会被调用。
    10. // 这就像监听一个未被定义的计算属性
    11. return this.a + this.b
    12. },
    13. function (newVal, oldVal) {
    14. // 做点什么
    15. }
    16. )

    vm.$watch 返回一个取消观察函数,用来停止触发回调:

    1. var unwatch = vm.$watch('a', cb)
    2. // 之后取消观察
    3. unwatch()
  • 选项:deep
    为了发现对象内部值的变化,可以在选项参数中指定 deep: true。注意监听数组的变更不需要这么做。

    1. vm.$watch('someObject', callback, {
    2. deep: true
    3. })
    4. vm.someObject.nestedValue = 123
    5. // callback is fired
  • 选项:immediate
    在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调:

    1. vm.$watch('a', callback, {
    2. immediate: true
    3. })
    4. // 立即以 `a` 的当前值触发回调
  • 注意在带有 immediate 选项时,你不能在第一次回调时取消侦听给定的 property。

    1. // 这会导致报错
    2. var unwatch = vm.$watch(
    3. 'value',
    4. function () {
    5. doSomething()
    6. unwatch()
    7. },
    8. { immediate: true }
    9. )
  • 如果你仍然希望在回调内部调用一个取消侦听的函数,你应该先检查其函数的可用性:

    1. var unwatch = vm.$watch(
    2. 'value',
    3. function () {
    4. doSomething()
    5. if (unwatch) {
    6. unwatch()
    7. }
    8. },
    9. { immediate: true }
    10. )

    computed和watch的区别

    computed就是计算属性,watch就是监听。
    computed在调用的时候不需要加括号,可以当属性用 ;会根据依赖自动缓存,如果依赖不变computed值就不会再重新计算。
    watch是用来监听的,如果某个属性变化了就执行一个函数,有两个选项immediate是否在第一次渲染的时候执行这个函数;deep如果监听一个对象,是否要看对象里面属性的变化。