Propriétés calculées et écouteurs


Table des matières


Propriétés calculées


Les expressions dans les modèles sont très pratique, mais l'intention originale de leur conception est utilisée pour des raisons simples opérationnel. Mettre trop de logique dans un modèle peut le rendre trop lourd et difficile à maintenir. Par exemple :

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

Ici, les modèles ne sont plus une simple logique déclarative. Il faut regarder un moment pour se rendre compte qu'ici on veut afficher la chaîne inversée de la variable message. Cela devient plus difficile à gérer lorsque vous souhaitez référencer la chaîne inversée ici plusieurs fois dans le modèle. message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。

所以,对于任何复杂逻辑,你都应当使用计算属性


基础例子

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

结果:

Original message: "Hello"
Computed reversed message: "olleH"

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

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

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

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


计算属性缓存 vs 方法

你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果:

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

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

Donc, pour toute logique complexe, vous devez utiliser 🎜Propriétés calculées🎜. 🎜🎜🎜🎜

🎜Exemple de base🎜🎜🎜

computed: {
  now: function () {
    return Date.now()
  }
}
<div id="demo">{{ fullName }}</div>
🎜Résultat : 🎜
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})
🎜Ici, nous déclarons une propriété calculée reversedMessage< / code>. La fonction que nous fournissons sera utilisée comme fonction getter de la propriété vm.reversedMessage : 🎜🎜
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
🎜Vous pouvez ouvrir la console du navigateur et modifier vous-même la vm dans l'exemple. La valeur de vm.reversedMessage dépend toujours de la valeur de vm.message. 🎜🎜Vous pouvez lier les propriétés calculées dans des modèles, tout comme les propriétés normales. Vue sait que vm.reversedMessage dépend de vm.message, donc lorsque vm.message change, toutes les dépendances sur vm.reversedMessage< La liaison pour /code> sera également mise à jour. Et le meilleur, c'est que nous avons créé cette dépendance de manière déclarative : la fonction getter de la propriété calculée n'a aucun effet secondaire, ce qui la rend plus facile à tester et à comprendre. 🎜🎜🎜🎜

🎜Mise en cache des propriétés calculées vs méthode 🎜🎜🎜🎜Vous avez peut-être remarqué que nous pouvons réussir Pour atteindre l'objectif même effet, appelez une méthode dans : 🎜

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...
<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
🎜 On peut définir la même fonction comme une méthode au lieu d'une propriété calculée. Le résultat final est en effet exactement le même dans les deux sens. Toutefois, la différence réside dans le fait que les propriétés calculées sont mises en cache en fonction de leurs dépendances réactives. Ils ne sont réévalués que lorsque les dépendances réactives associées changent. Cela signifie que tant que message n'a pas changé, les accès multiples à la propriété calculée reversedMessage renverront immédiatement le résultat du calcul précédent sans avoir à réexécuter la fonction. 🎜

Cela signifie également que la propriété calculée ci-dessous ne sera plus mise à jour, car Date.now() n'est pas une dépendance réactive : Date.now() 不是响应式依赖:

<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
    // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
    // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
    // 请参考:https://lodash.com/docs#debounce
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
    }
  }
})
</script>

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

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


计算属性 vs 侦听属性

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。细想一下这个例子:

rrreeerrreee

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

rrreee

好得多了,不是吗?


计算属性的 setter

计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :

rrreee

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstNamevm.lastName 也会相应地被更新。


侦听器


虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

例如:

rrreeerrreee

结果:

1.gif

在这个示例中,使用 watch 选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

除了 watchrrreeeEn revanche, chaque fois qu'un nouveau rendu est déclenché, la méthode est appelée. La fonction seratoujours exécutée à nouveau.

Pourquoi avons-nous besoin de mise en cache ? Supposons que nous ayons une propriété calculée coûteuse A qui nécessite de parcourir un vaste tableau et d'effectuer de nombreux calculs. Nous pourrions alors avoir d'autres propriétés calculées qui dépendent de A . Sans mise en cache, nous exécuterions inévitablement le getter de A plusieurs fois ! Si vous ne souhaitez pas de mise en cache, utilisez plutôt des méthodes.


Propriétés calculées vs propriétés d'écoute

🎜Vue fournit un manière plus générale d'observer et de répondre aux changements de données sur les instances Vue : Propriétés d'écoute. Lorsque certaines données doivent changer à mesure que d'autres données changent, il est facile d'abuser de watch - surtout si vous avez déjà utilisé AngularJS. Cependant, il est souvent préférable d'utiliser des propriétés calculées plutôt que des rappels impératifs watch. Prenons cet exemple : 🎜rrreeerrreee🎜Le code ci-dessus est impératif et répétitif. Comparez-le à la version de la propriété calculée : 🎜rrreee🎜 Bien mieux, n'est-ce pas ? 🎜🎜🎜🎜

Setter pour les propriétés calculées🎜🎜Les propriétés calculées n'ont que des getters par défaut, mais dans Vous pouvez également fournir un setter si nécessaire : 🎜rrreee🎜Maintenant, lors de l'exécution de vm.fullName = 'John Doe', le setter sera appelé, vm.firstName et vm.lastName sera également mis à jour en conséquence. 🎜🎜🎜🎜

Listener


🎜Pendant le calcul des propriétés sont plus approprié dans la plupart des cas, mais parfois un écouteur personnalisé est nécessaire. C'est pourquoi Vue propose un moyen plus général de répondre aux modifications de données via l'option watch. Cette approche est particulièrement utile lorsque vous devez effectuer des opérations asynchrones ou coûteuses lorsque les données changent. 🎜🎜Par exemple : 🎜rrreeerrreee🎜Résultat : 🎜🎜 1.gif🎜🎜Dans cet exemple, l'utilisation de l'option watch nous permet d'effectuer une opération asynchrone (accéder à une API), de limiter la fréquence à laquelle nous effectuons l'opération et d'attendre d'obtenir le résultat final. Définir un état intermédiaire. Ce sont des choses que les propriétés calculées ne peuvent pas faire. 🎜🎜En plus de l'option watch, vous pouvez également utiliser l'impérative 🎜vm.$watch API🎜. 🎜🎜🎜🎜🎜 🎜