vue-常用特性-计算属性和侦听器

vue-常用特性-计算属性和侦听器

目录




内容

1、计算属性

1.1、API

computed

  • 类型:

      { [key: string]: Function | { get: Function, set: Function } }
    
  • 详细:

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

注意如果你为一个计算属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。

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

计算属性的结果会被缓存,除非依赖的响应式 property 变化才会重新计算。注意,如果某个依赖 (比如非响应式 property) 在该实例范畴之外,则计算属性是不会被更新的。

  • 示例:

      var vm = new Vue({
        data: { a: 1 },
        computed: {
      	// 仅读取
      	aDouble: function () {
      	  return this.a * 2
      	},
      	// 读取和设置
      	aPlus: {
      	  get: function () {
      		return this.a + 1
      	  },
      	  set: function (v) {
      		this.a = v - 1
      	  }
      	}
        }
      })
      vm.aPlus   // => 2
      vm.aPlus = 3
      vm.a       // => 2
      vm.aDouble // => 4
    

1.2、适用场景

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

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

在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 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。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数:

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

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

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

1.3、计算属性缓存 vs 方法

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

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

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

这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

computed: {
  now: function () {
	return Date.now()
  }
}

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

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

1.4、计算属性 vs 侦听属性

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

<div id="demo">{{ fullName }}</div>
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
	}
  }
})

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

var vm = new Vue({
  el: '#demo',
  data: {
	firstName: 'Foo',
	lastName: 'Bar'
  },
  computed: {
	fullName: function () {
	  return this.firstName + ' ' + this.lastName
	}
  }
})

好得多了,不是吗?

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

// ...
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]
	}
  }
}
// ...

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

2、侦听器

2.1、API

watch

  • 类型:

      { [key: string]: string | Function | Object | Array }
    
  • 详细:

一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个 property。

  • 示例:

      var vm = new Vue({
        data: {
      	a: 1,
      	b: 2,
      	c: 3,
      	d: 4,
      	e: {
      	  f: {
      		g: 5
      	  }
      	}
        },
        watch: {
      	a: function (val, oldVal) {
      	  console.log('new: %s, old: %s', val, oldVal)
      	},
      	// 方法名
      	b: 'someMethod',
      	// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
      	c: {
      	  handler: function (val, oldVal) { /* ... */ },
      	  deep: true
      	},
      	// 该回调将会在侦听开始之后被立即调用
      	d: {
      	  handler: 'someMethod',
      	  immediate: true
      	},
      	// 你可以传入回调数组,它们会被逐一调用
      	e: [
      	  'handle1',
      	  function handle2 (val, oldVal) { /* ... */ },
      	  {
      		handler: function handle3 (val, oldVal) { /* ... */ },
      		/* ... */
      	  }
      	],
      	// watch vm.e.f's value: {g: 5}
      	'e.f': function (val, oldVal) { /* ... */ }
        }
      })
      vm.a = 2 // => new: 2, old: 1
    

注意,不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.updateAutocomplete 将是 undefined。

2.2、适用场景

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

例如:

<div id="watch-example">
  <p>
	Ask a yes/no question:
	<input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
	<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>

结果:

Ask a yes/no question: 

I cannot give you an answer until you ask a question!

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

后记

vue官网地址:https://cn.vuejs.org/

本项目为参考某马视频开发,相关视频及配套资料可自行度娘或者联系本人。上面为自己编写的开发文档,持续更新。欢迎交流,本人QQ:806797785

前端项目源代码地址:https://gitee.com/gaogzhen/vue
    后端JAVA源代码地址:https://gitee.com/gaogzhen/JAVA

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gaog2zh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值