计算属性 computed

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

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

在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多包含此处的翻转字符串时,就会更加难以处理。
所以,对于任何复杂逻辑,你都应当使用计算属性

🌰:

  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. })

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

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

vm.reversedMessage 的值始终取决于 vm.message 的值。

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

所以其实计算属性就相当于根据data中已有的property经过计算后得到新的property。在使用上也和property一样,双花括号语法绑定即可。

  1. editData.businessData.attributeColumnOptions = computed(
  2. () => editData.businessData.entityColumnOptions
  3. )
  4. editData.businessData.sourceOptions = computed(
  5. () => editData.businessData.entityColumnOptions
  6. // editData.businessData.entityColumnOptions.filter((item: any) => {
  7. // return !(editData.businessData.entityColumnSelected == item)
  8. // })
  9. )

计算属性缓存

计算属性是基于它们的响应式依赖进行缓存的。也就是说只要计算属性所依赖的 property 的值没有改变,计算属性会立即返回之前的计算结果,而不必再次执行getter函数。

计算属性的setter

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

  1. // ...
  2. computed: {
  3. fullName: {
  4. // getter
  5. get: function () {
  6. return this.firstName + ' ' + this.lastName
  7. },
  8. // setter
  9. set: function (newValue) {
  10. var names = newValue.split(' ')
  11. this.firstName = names[0]
  12. this.lastName = names[names.length - 1]
  13. }
  14. }
  15. }
  16. // ...

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

侦听器 watch

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

  1. <div id="watch-example">
  2. <p>
  3. Ask a yes/no question:
  4. <input v-model="question">
  5. </p>
  6. <p>{{ answer }}</p>
  7. </div>
  8. <!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
  9. <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
  10. <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
  11. <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
  12. <script>
  13. var watchExampleVM = new Vue({
  14. el: '#watch-example',
  15. data: {
  16. question: '',
  17. answer: 'I cannot give you an answer until you ask a question!'
  18. },
  19. watch: {
  20. // 如果 `question` 发生改变,这个函数就会运行
  21. question: function (newQuestion, oldQuestion) {
  22. this.answer = 'Waiting for you to stop typing...'
  23. this.debouncedGetAnswer()
  24. }
  25. },
  26. created: function () {
  27. // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
  28. // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
  29. // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
  30. // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
  31. // 请参考:https://lodash.com/docs#debounce
  32. this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  33. },
  34. methods: {
  35. getAnswer: function () {
  36. if (this.question.indexOf('?') === -1) {
  37. this.answer = 'Questions usually contain a question mark. ;-)'
  38. return
  39. }
  40. this.answer = 'Thinking...'
  41. var vm = this
  42. axios.get('https://yesno.wtf/api')
  43. .then(function (response) {
  44. vm.answer = _.capitalize(response.data.answer)
  45. })
  46. .catch(function (error) {
  47. vm.answer = 'Error! Could not reach the API. ' + error
  48. })
  49. }
  50. }
  51. })
  52. </script>

watch高阶使用

立即执行

watch 是在监听属性改变时才会触发,有些时候,我们希望在组件创建后 watch 能够立即执行

可能想到的的方法就是在 create 生命周期中调用一次,但这样的写法不优雅,或许我们可以使用这样的方法

  1. export default {
  2. data() {
  3. return {
  4. name: 'Joe'
  5. }
  6. },
  7. watch: {
  8. name: {
  9. handler: 'sayName',
  10. immediate: true
  11. }
  12. },
  13. methods: {
  14. sayName() {
  15. console.log(this.name)
  16. }
  17. }
  18. }

深度监听

在监听对象时,对象内部的属性被改变时无法触发 watch ,我们可以为其设置深度监听

  1. export default {
  2. data: {
  3. studen: {
  4. name: 'Joe',
  5. skill: {
  6. run: {
  7. speed: 'fast'
  8. }
  9. }
  10. }
  11. },
  12. watch: {
  13. studen: {
  14. handler: 'sayName',
  15. deep: true
  16. }
  17. },
  18. methods: {
  19. sayName() {
  20. console.log(this.studen)
  21. }
  22. }
  23. }
  1. //监听对象某个值的变化
  2. watch:{
  3. "xxx.value":(newVal,oldVal)=>{ // xxx.value是data里对象的value
  4. }
  5. }

computed 和 watch 的区别

计算属性 computed:

  • 支持缓存,只有依赖数据发生改变,才会重新进行计算
  • 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
  • computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
  • 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。

    侦听属性 watch:

  • 不支持缓存,数据变,直接会触发相应的操作;

  • watch支持异步;
  • 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
  • 当一个属性发生变化时,需要执行对应的操作;一对多;
  • 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数: