vue--计算属性(computed)vs事件(methods)vs监听器(watch)

关于 computed 和 watch 的差异:

    1.computed 是计算一个新的属性,并将该属性挂载到 vm(Vue 实例)上,而 watch 是监听已经存在且已挂载到 vm 上的数据,所以用 watch 同样可以监听 computed 计算属性的变化(其它还有 data、props)
    2.computed 本质是一个惰性求值的观察者,具有缓存性,只有当依赖变化后,才会计算新的值,computed 第一次加载就监听,而 watch 则是当数据发生变化便会调用执行函数
    3.从使用场景上说,computed 适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据

一.计算属性(computed)

应用场景:在一个计算属性里可以完成各种复杂的逻辑,包括运算、函数调用等,只要最终返回一个结果就可以。

<template>
    <div class="box">
        <h1>计算属性</h1>
        <hr>
        <!-- 直接渲染得到数据 -->
        <p><input type="text" v-model="message"></p>
        <!-- 对data的数据进行简单的操作
        这样有一个不好的地方就是后期对页面数据的维护不是那么方便 -->
        <p>{{message.toUpperCase()}}</p>
        <!-- 通过计算属性改变mesage进行渲染 -->
        <p>{{computedMessage}}</p>
        <hr>
        <!-- 通过事件来改变属性 -->
        <span>{{message}}</span><button @click="changeMessage()">点击改变</button>
        <hr>
    </div>
</template>
<script>
export default {
    data() {
        return {
            message: 'Hello world!',
            passw2: 'sss',
        }
    },
    // computed属性是data返回值,在使用的时候只使用函数名不是调用函数
    computed:{//计算属性相当于data里的属性
              //什么时候执行:初始化显示/相关的data属性发生变化
        computedMessage(){//计算属性中的get方法,方法的返回值就是属性值
            return this.message.split('')
        },
        // computed属性get和set的使用
        getAndsetMsg: {
            get() {//回调函数 当需要读取当前属性值时执行,根据相关数据依赖计算并返回新的属性值
                return this.message
            },
            set(val) {//计算属性一般是没有set方法, 只读属性.
                      //监视当前属性值的变化,当属性值发生变化时执行,更新相关的属性数据
	              //val就是getAndsetMsg的最新属性值
                this.message = val+ 'muzidigbig'    
            }
        }
    },
    methods:{
        changeMessage(){
            this.message = this.computedMessage+'altman'
        }
    },
}
</script>
 

set可以监听当前值的变化,然后赋值给相应的依赖数据,得到新值

这里需要注意的是computed中的函数名不能和所操作data中的数据一样;computed中的函数名相当于是操作data数据后的新数据,在模块中直接使用这个函数名即可实现对data中数据改变的渲染。

 注意,不应该使用箭头函数来定义 computed (例如 computedMessage: () => { return XXX;})。
 理由:箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,严格模式下this 将是 undefined。

优点:计算属性会进行缓存,如果多次使用使用,计算属性只会调用一次。 

二.事件(methods)

应用场景:函数的调用

<template>
  <div class="on">
    <!-- v-on 就是监听事件,可以用v-on指令监听DOM事件来触发一些javascript代码。
两中事件监听器(无参,有参);事件的行内调用
    v-on 还有一种简单的写法,就是用@代替-->
    <div @click="count++">点击添加1:{{count}}</div>
    <!-- 如果同时传入某个参数,同时需要event时,可以通过$event传入事件 -->
    <!-- 
    .stop - 调用 event.stopPropagation()。
    .prevent - 调用 event.preventDefault()。
    .capture - 添加事件侦听器时使用 capture 模式。
    .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
    .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
    .native - 监听组件根元素的原生事件。
    .once - 只触发一次回调。
    .left - (2.2.0) 只当点击鼠标左键时触发。
    .right - (2.2.0) 只当点击鼠标右键时触发。
    .middle - (2.2.0) 只当点击鼠标中键时触发。
    .passive - (2.3.0) 以 { passive: true } 模式添加侦听器
    -->
    <button type="button" @click.prevent="init(10,$event)">初始值</button>
    <button type="button" @click="add()">+1</button>
    <button type="button" @click="substrack()">-1</button>
    <h2>{{countNum}}</h2>
  </div>
</template>
<script>
export default {
  name: "on",
  data() {
    return {
      count: 0,
      countNum: 0
    };
  },
  methods: {
    /** 
     * 注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。
     * 理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。
    */
    init(num, event) {
      this.countNum = num;
      console.log(event);
    },
    add() {
      return this.countNum++;
    },
    // substrack: () => {
    //   return this.countNum--; // undefined
    // }
    substrack() {
      return this.countNum--;
    }
  }
};
</script>

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

三.监听器(watch)

应用场景:数据变化时执行异步或开销较大的操作。

首先确认 watch是一个对象,一定要当成对象来用。
对象就有键,有值。
第一种 键:就是你要监听的那个数据,比如说$route,这个就是要监控路由的变化。或者是data中的某个变量。
值可以是函数:就是当你监控的家伙变化时,需要执行的函数,这个函数有两个形参,第一个是改变后的新值,第二个是改变之前的值。
第二种  值也可以是函数名:不过这个函数名要用单引号来包裹。(不常用)
第三种情况厉害了(监听的那个数据值是一个对象):
值是包括选项的对象:选项包括有三个。

    第一个handler:其值是一个回调函数。即监听到变化时应该执行的函数。
    第二个是deep:其值是true或false;确认是否深入监听。(监听 对象/数组里面值的变化)。
    第三个是immediate:其值是true或false;确认是否以当前的初始值执行handler的函数。
 

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

用法一:基础用法

<template>
    <div class="box">
        <h1>监听器</h1>
        <label>二次密码:</label>
        <input v-model="watchMsg"  />
    </div>
</template>
<script>
export default {
    data() {
        return {
            message: 'Hello world!',
            watchMsg: '我是监听操作',
        }
    },
    watch:{
        watchMsg(newVal,oldVal){
            console.log('newVal:'+newVal,'oldVal:'+oldVal);
        },
    }
}
</script>

用法二:handler方法和immediate属性

<template>
    <div class="box">
        <h1>监听器</h1>
        <label>二次密码:</label>
        <input v-model="watchMsg"  />
    </div>
</template>
<script>
export default {
    data() {
        return {
            message: 'Hello world!',
            watchMsg: '我是监听操作',
        }
    },
    watch:{
        watchMsg: {
            handler(newVal,oldVal){
                console.log('newVal:'+newVal,'oldVal:'+oldVal);
            },
            immediate:true,
        }
    }
}
</script>

注意到handler了吗,我们给 watchMsg绑定了一个handler方法,之前我们写的 watch 方法其实默认写的就是这个handler,Vue.js会去处理这个逻辑,最终编译出来其实就是这个handler

immediate:true代表如果在 wacth 里声明了watchMsg之后,就会立即先去执行里面的handler方法,如果为 false就跟我们以前的效果一样,不会在绑定的时候就执行。

用法三:

deep属性

watch 里面还有一个属性 deep,默认值是 false,代表是否深度监听,比如我们 data 里有一个obj属性/数组:

<template>
    <div class="box">
        <h1>监听器</h1>
        <label>二次密码:</label>
        <input v-model="obj.watchMsg"  />
    </div>
</template>
<script>
export default {
    data() {
        return {
            message: 'Hello world!',
            obj:{
                watchMsg: '我是监听操作',
            }
        }
    },
    watch:{
        // 需要注意的是这里的监听对象也需要变化
        'obj.watchMsg': {
            handler(newVal,oldVal){
                console.log('newVal:'+newVal,'oldVal:'+oldVal);
            },
            immediate:true,
            deep:true,
        }
    }
}
</script>

2、数组的watch

<template>
    <div class="box">
        <h1>监听器</h1>
        <p>{{array}}</p>
        <button @click='addArray()'>点击改变数组</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            message: 'Hello world!',
            array:['mu','zi','dig','big']
        }
    },
    watch:{
        // 需要注意的是这里所监听的对象应该是数组
        array:{
            handler(newVal,oldVal){
                console.log(newVal+'--'+oldVal)
            },
            deep:true,
            immediate:true,
        }
    },
    methods:{
        addArray() {
            this.array.push('66')
        }
    }
}
</script>

3、数组中对象的watch

<template>
    <div class="box">
        <h1>监听器</h1>
        <ul>
            <li v-for="(item,index) in arrayObj" :key="item.id">{{index}}--{{item.name}}---<input type="text" v-model="item.age"  @keydown="change(index)"></li>
        </ul>
        <hr>
        <ul>
            <li v-for="(item,index) in arrayObj" :key="item.id">{{index}}--{{item.name}}---{{item.age}}</li>
        </ul>
    </div>
</template>
<script>
export default {
    data() {
        return {
            arrayObj:[
                {name:'张三',age:'23'},
                {name:'李四',age:'22'}
            ],
        }
    },
    watch:{
        // 需要注意的是这里所监听的对象应该是数组
        arrayObj:{
            handler(newVal,oldVal){
                console.log(newVal+'--'+oldVal)
            },
            deep:true,
            immediate:true,
        }
    },
    methods:{
        change(i) {
            // console.log(this.changeValue)
            this.arrayObj[i].age = this.arrayObj[i].age
        }
    }
}
</script>

 4、对象具体属性的watch[活用computed]

<template>
    <div class="box">
        <h1>监听器</h1>
        <p>{{obj.name}}</p>
        <input type="text" v-model="obj.name" name="" id="">
        <p>{{newName}}</p>
    </div>
</template>
<script>
export default {
    data() {
        return {
            obj:{name:'muzi',age:'23'},
        }
    },
    computed:{
        newName(){
            return this.obj.name.toUpperCase();
        }
    },
    watch:{
        newName(newVal,oldVal){
            console.log(newVal+'--'+oldVal)
        },
        // newName:{
        //     handler(newVal,oldVal){
        //         console.log(newVal+'--'+oldVal)
        //     },
        //     deep:true,
        //     immediate:true,
        // }
    },
}
</script>

从效果图上可以看出,计算属性最好使用在更改数据上然后进行渲染;先进行的计算属性再进行的监听。

1.1 computed

    1.是计算值
    2.具有缓存性,页面重新渲染值不会变化,计算属性会立即返回之前的计算结果,而不必再次执行函数(只有当依赖数据发生改变才会重新计算)
    3.不支持异步,当computed内有异步操作时无效,无法监听数据的变化

    4.不支持箭头函数

    5.第一次加载就监听,必须 return

        理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,我们这里需要挂载到vm上。

2.2 watch

    1.是观察的动作

    2.无缓存性,页面重新渲染时值不变化也会执行(值改变则直接触发)

    3.支持异步

    4.监听数据必须是 data 中声明过或者父组件传递过来的props中的数据以及挂载到vm上的数据(computed)

    5.不支持箭头函数

    6.无需return

        理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,我们这里需要挂载到vm上。

2.3 methods

    1.是函数

    2.无缓存性,页面重新渲染会被重新调用

    3.支持异步

    4.不支持箭头函数

        理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,我们这里需要挂载到vm上。

下面还另外总结了几点关于 computed 、watch 和 methods 的差异:

  1. computed 是计算一个新的属性,并将该属性挂载到 vm(Vue 实例)上,而 watch 是监听已经存在且已挂载到 vm 上的数据,所以用 watch 同样可以监听 computed 计算属性的变化(其它还有 dataprops
  2. computed 本质是一个惰性求值的观察者,具有缓存性,只有当依赖变化后,才会计算新的值,computed 第一次加载就监听,其他直接使用这个计算得到的值,而 watch 则是当数据发生变化便会调用执行函数
  3. 计算属性是有缓存的,只要相关依赖没有改变,多次访问计算属性得到的值是之前缓存的计算结果,不会多次执行
  4. methods则是调用才更新
  5. 从使用场景上说,computed 适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据


区别:

1.watch和computed都是以Vue的依赖追踪机制为基础的,它们都试图处理这样一件事情:当某一个数据(称它为依赖数据)发生变化的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动。
2.对methods:methods里面是用来定义函数的,很显然,它需要手动调用才能执行。而不像watch和computed那样,“自动执行”预先定义的函数
【总结】:methods里面定义的函数,是需要主动调用的,而和watch和computed相关的函数,会自动调用,完成我们希望完成的作用

若有不足请多多指教!希望给您带来帮助!


版权声明:本文为muzidigbig原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。