Method&&Computed&&Watch

147 阅读2分钟

Computed(计算属性)

类型

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

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

使用

我们很多人喜欢在模板上直接把想要的结果通过运算呈现出来,比如:

//直接实现字符串的翻转
import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
data :{
  message:"Hello"
},
template: `
<div>
newMessage:{{message.split('').reverse().join('')}}
</div>
`
}).$mount("#app");

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,当你想要在模板中多包含此处的翻转字符串时,就会更加难以处理。所以,对于任何复杂逻辑,你都应当使用计算属性

代码如下:

//使用计算属性实现字符串翻转
import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

const vm=new Vue({
data :{
  message:"Hello"
 },

computed:{
//计算属性的getter
 reversedMessage(){
return this.message.split('').reverse().join('')
 }
},
template: `
<div>
newMessage:{{reversedMessage}}
</div>
`
}).$mount("#app");

这里我们申明了一个计算属性reversedMessage,我们提供的函数用作propertyreversedMessage的getter函数:

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

Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。

计算属性VS方法

上面的效果我们也可以用方法来实现:

//通过方法来实现字符串翻转
import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
data :{
  message:"Hello"
 },

methods:{
 reversedMessage(){
return this.message.split('').reverse().join('')
 }
},
template: `
<div>
newMessage:{{reversedMessage()}}
</div>
`
}).$mount("#app");

他们得到的函数是一样的,结果也是一样的,执行就是相差一个( )而已,然而他们最大的区别就是计算属性基于它的响应式依赖进行缓存的,只在相关响应式依赖发生改变时才会重新求值,这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

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

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

计算属性的 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.firstNamevm.lastName 也会相应地被更新。

watch(侦听属性)

类型

{ [key: string]: string | Function | Object | Array }

语法

watch: {
o1: () => {} // 别用箭头函数,这里的this是全局对象

o2: function(value, oldValue){},
o3(){},
o4: [f1, f2],
o5: 'methodName',
o6: {handler:fn, deep:true, immediate:true},
'object.a': function(){}
}

使用

当数据变化时,执行一个函数,比如:

//实现数据的变化及撤销
import Vue from "vue/dist/vue.js";

Vue.config.productionTip = false;

new Vue({
data :{
  n:0,
  history:[],
  inUndoMode:false
 },
watch:{
  n:function(newVulue,oldVulue){
    if(!this.inUndoMode){
      this.history.push({from:oldVulue,to:newVulue})
    }
  },
},
methods:{
add1(){
  this.n+=1
},
add2(){
  this.n+=2
},
minus1(){
  this.n-=1
},
minus2(){
  this.n-=2
},
undo(){
  const last =this.history.pop()
  this.inUndoMode=true
  const old =last.from
  this.n=old//watch 里面的n是异步的
  this.$nextTick(() => {
    this.inUndoMode = false
  })
}
},
template: `
<div>{{n}}
<hr>
<button @click="add1">+1</button>
<button @click="add2">+2</button>
<button @click="minus1">-1</button>
<button @click="minus2">-2</button>
<hr>
<button @click="undo">撤销</button>
<hr>
{{history}}
</div>

`
}).$mount("#app");

运行动画

使用 watch 的深度遍历(deep)和立即调用(inmediate)功能

deep 设置为 true 用于监听对象内部值的变化

immediate 设置为 true 将立即以表达式的当前值触发回调

<template>
    <button @click="obj.a = 2">修改</button>
</template>
<script>
export default {
    data() {
        return {
            obj: {
                a: 1,
            }
        }
    },
    watch: {
        obj: {
            handler: function(newVal, oldVal) {
                console.log(newVal); 
            },
            deep: true,
            immediate: true 
        }
    }
}
</script>

以上代码我们修改了 obj 对象中 a 属性的值,我们可以触发其 watch 中的 handler 回调输出新的对象,而如果不加 deep: true,我们只能监听 obj 的改变,并不会触发回调。同时我们也添加了 immediate: true 配置,其会立即以 obj 的当前值触发回调。

两者之间对比

image.png

从上面流程图中,我们可以看出它们之间的区别:

  • computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才会重新计算。
  • watch:监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操作。

除此之外,有点很重要的区别是:计算属性不能执行异步任务,计算属性必须同步执行。也就是说计算属性不能向服务器请求或者执行异步任务。如果遇到异步任务,就交给侦听属性。watch也可以检测computed属性。

参考:Vue中文文档
详解Vue计算属性和侦听属性