methods

我们用methods选项向组件实例添加方法,它应该是一个包含所需方法的对象

声明的方法可以直接通过组件实例访问,或者在模板语法表达式中使用。所有的方法都会将它们的 this 上下文自动绑定为组件实例,即使在传递时也如此。

在声明方法时避免使用箭头函数,因为它们不能通过 this 访问组件实例。

export default {
  data() {
    return { a: 1 }
  },
  methods: {
    plus() {
      this.a++
    }
  }
}

这些methods和组件实例的其他所有property一样可以在组件的模板中被访问。在模板中它们通常被当做事件监听使用

<button @click="plus">up plus</button>

在上面的例子中,点击 button 时,会调用 plus方法
也可以直接从模板中调用方法。通常换做计算属性会更好。但是,在计算属性不可行的情况下,使用方法可能会很有用。你可以在模板支持 JavaScript 表达式的任何地方调用方法:

<span :title="toTitleDate(date)">
  {{ formatDate(date) }}
</span>

如果 toTitleDateformatDate 访问了任何响应式数据,则将其作为渲染依赖项进行跟踪,就像直接
在模板中使用过一样。

从模板调用的方法不应该有任何副作用,比如更改数据或触发异步进程。如果你想这么做,应该使用生命
周期钩子来替换。

computed

基础示例

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。比如说,我们有这样一个包含嵌套数组的对象:

export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  }
}

我们想根据 author 是否已有一些书籍来展示不同的信息:

<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>

这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于 author.books。更重要的是,如果在模板中需要不止一次这样的计算,我们可不想将这样的代码在模板里重复好多遍。

因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:

export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    // 计算属性,只要依赖值不变,就不会重新计算
    publishedBooksMessage() {
      // `this` 指向当前组件实例
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}


<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>

我们在这里定义了一个计算属性 publishedBooksMessage

更改此应用的 databooks 数组的值后,可以看到 publishedBooksMessage 也会随之改变。

在模板中使用计算属性的方式和一般的属性并无二致。Vue 会检测到 this.publishedBooksMessage 依赖于 this.author.books,所以当 this.author.books 改变时,任何依赖于 this.publishedBooksMessage 的绑定都将同时更新。

methods VS computed

你可能注意到我们在表达式中像这样调用一个函数也会获得和计算属性相同的结果:

export default {
  data() {
    return {
      author: {
        name: 'John Doe',
        books: [
          'Vue 2 - Advanced Guide',
          'Vue 3 - Basic Guide',
          'Vue 4 - The Mystery'
        ]
      }
    }
  },
  computed: {
    BooksMessageComputed() {
      console.log('computed计算属性')
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  },
  methods:{
    BooksMessageMethods() {
      console.log('methods方法')
      return this.author.books.length > 0 ? 'Yes' : 'No'
    }
  }
}

若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 BooksMessageComputed 都会立即返回先前的计算结果,而不用重复执行 getter 函数。
WX20221223-042752@2x.png

相比之下,每当触发重新渲染时,调用方法将始終会再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 list ,它需要遍历一个巨大的数组并
做大量的计算。然后我们可能有其他的计算属性依赖于 list。如果没有缓存,我们将不可避免的多次执
listgetter !如果你不希望有缓存,请用 method 来替代

可写计算属性

计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到"可写"的属性,你可以通过同时提供 gettersetter 来创建:

export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    fullName: {
      // getter
      get() {
        return this.firstName + ' ' + this.lastName
      },
      // setter
      set(newValue) {
        // 注意:我们这里使用的是解构赋值语法
        [this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  }
}

现在当你再运行 this.fullName = 'John Doe' 时,setter 会被调用而 this.firstNamethis.lastName 会随之更新。

Getter 不应有副作用
计算属性的 getter 应只做计算而没有任何其他的副作用,这一点非常重要,请务必牢记。举例来说,不要在 getter 中做异步请求或者更改 DOM!一个计算属性的声明中描述的是如何根据其他值派生一个值。因此 getter 的职责应该仅为计算和返回该值。在之后的指引中我们会讨论如何使用监听器根据其他响应式状态的变更来创建副作用。

避免直接修改计算属性值
从计算属性返回的值是派生状态。可以把它看作是一个"临时快照",每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。

最后修改:2022 年 12 月 23 日
如果觉得我的文章对你有用,请随意赞赏