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: {
// 计算属性的 getter
reversedMessage: 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 // => 2
vm.aPlus = 3
vm.a // => 2
vm.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() { }, //省略了:function
c: [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如果监听一个对象,是否要看对象里面属性的变化。