computed -计算属性
被计算出来的属性就是计算属性
- 类型:
{ [key: string]: Function | { get: Function, set: Function } }
下面这段代码想要显示变量 message 的翻转字符串,但在模板中放入太多的逻辑让模板过重且难以维护。
<div id="example">{{ message.split('').reverse().join('') }}</div>
基础例子
我们声明了一个计算属性 reversedMessage。我们提供的函数将用作 property vm.reversedMessage 的 getter 函数:
<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: {// 计算属性的 getterreversedMessage: function () {// `this` 指向 vm 实例return this.message.split('').reverse().join('')}}})
结果:
Original message: “Hello”
Computed reversed message: “olleH”
可以打开浏览器的控制台,自行修改例子中的 vm。vm.reversedMessage 的值始终取决于 vm.message 的值。
console.log(vm.reversedMessage) // => 'olleH'vm.message = 'Goodbye'console.log(vm.reversedMessage) // => 'eybdooG'
你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
计算属性方法
可以通过在表达式中调用方法来达到同样的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中methods: {reversedMessage: function () {return this.message.split('').reverse().join('')}}
将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新计算。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
计算属性缓存
计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
注意如果你为一个计算属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。
computed: {aDouble: vm => vm.a * 2}
计算属性的结果会被缓存,除非依赖的响应式 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 // => 2vm.aPlus = 3vm.a // => 2vm.aDouble // => 4
参考:计算属性
注意,如果某个依赖 (比如非响应式 property) 在该实例范畴之外,则计算属性是不会被更新的。这意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:
computed: {now: function () {return Date.now()}}
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
为什么需要缓存?
假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
如果依赖的属性没有变化,就不会重新计算
getter/setter默认不会做缓存,Vue做了特殊处理
watch
监听/侦听
**
Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
watch有两种语法,他们是等价的
语法1
- 类型:
{ [key: string]: string | Function | Object | Array } 详细:
一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用$watch(),遍历 watch 对象的每一个 property。watch:{a: function (value, oldValue) { },b() { }, //省略了:functionc: [f1, f2],d: 'methodName',e: { handler: fn, deep: true, immediate: true },'object.a': function () { },},
**
示例:
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-vm.$watch( expOrFn, callback, [options] )
- 参数:
{string | Function} expOrFn{Function | Object} callback{Object} [options]{boolean} deep{boolean} immediate
- 返回值:
{Function} unwatch - 用法:
观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数得到的参数为新值和旧值。表达式只接受简单的键路径。对于更复杂的表达式,用一个函数取代。
注意:在变更 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。
vm.$watch('xxx', fn, { deep:..., immediate:...})
示例:
// 键路径vm.$watch('a.b.c', function (newVal, oldVal) {// 做点什么})// 函数vm.$watch(function () {// 表达式 `this.a + this.b` 每次得出一个不同的结果时// 处理函数都会被调用。// 这就像监听一个未被定义的计算属性return this.a + this.b},function (newVal, oldVal) {// 做点什么})
vm.$watch返回一个取消观察函数,用来停止触发回调:var unwatch = vm.$watch('a', cb)// 之后取消观察unwatch()
选项:deep
为了发现对象内部值的变化,可以在选项参数中指定deep: true。注意监听数组的变更不需要这么做。vm.$watch('someObject', callback, {deep: true})vm.someObject.nestedValue = 123// callback is fired
选项:immediate
在选项参数中指定immediate: true将立即以表达式的当前值触发回调:vm.$watch('a', callback, {immediate: true})// 立即以 `a` 的当前值触发回调
注意在带有
immediate选项时,你不能在第一次回调时取消侦听给定的 property。// 这会导致报错var unwatch = vm.$watch('value',function () {doSomething()unwatch()},{ immediate: true })
如果你仍然希望在回调内部调用一个取消侦听的函数,你应该先检查其函数的可用性:
var unwatch = vm.$watch('value',function () {doSomething()if (unwatch) {unwatch()}},{ immediate: true })
computed和watch的区别
computed就是计算属性,watch就是监听。
computed在调用的时候不需要加括号,可以当属性用 ;会根据依赖自动缓存,如果依赖不变computed值就不会再重新计算。
watch是用来监听的,如果某个属性变化了就执行一个函数,有两个选项immediate是否在第一次渲染的时候执行这个函数;deep如果监听一个对象,是否要看对象里面属性的变化。
