Vue2(二):计算属性、监视属性、二者的区别

Vue2学习笔记:第二章

  • 一、计算属性
    • 1. 使用插值语法和methods拼接姓名
    • 2. 使用计算属性拼接姓名
    • 3. 计算属性总结
    • 4. 计算属性的简写
  • 二、监视属性
    • 1. 天气案例铺垫
    • 2. 引出监视属性
      • (1).new Vue时传入watch配置
      • (2).通过vm.$watch监视
    • 3. 深度监视
    • 4.监视的简写形式
  • 三、watch对比computed
    • 1.对比拼接姓名的案例
    • 2. 总结:

一、计算属性

顾名思义,计算属性就是计算出来的属性,英文名儿computed
这里要和data和methods里的东西区分,data里的属性,methods里的函数,你写的都会原封不动放到vm里,但是computed里面的东西写的是对象,最后放到vm里的是这个对象的名,值是get里的返回值。

1. 使用插值语法和methods拼接姓名

使用插值语法,如果简单点还行,如果复杂了,里面的逻辑写一大堆,很难看
使用methods不错,但是有一个问题,就是每次用到fullName都会调用一次,缓存太多


<div id="root">
    姓: <input type="text" v-model="firstName"><br><br>
    名: <input type="text" v-model="lastName"><br><br>
    
    
    姓名: <span>{{firstName.slice(0,3)}}-{{lastName}}span><br><br>


    
    姓名: <span>{{fullName()}}span><br><br>
    姓名: <span>{{fullName()}}span><br><br>
    姓名: <span>{{fullName()}}span><br><br>
    姓名: <span>{{fullName()}}span><br><br>
    姓名: <span>{{fullName()}}span>
div>

<script>
    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        methods: {
            fullName() {
                console.log('调用')
                return this.firstName + '-' + this.lastName;
            }
        }
    })
script>

2. 使用计算属性拼接姓名

1、没有缓存问题,第一次调用之后就不会再调用,节省内存
2、计算属性里面的属性要写成对象的形式,每个对象里面都有getter和setter
3、fullName实际上就是firstName和lastName经过一番计算得到的玩意儿,直接到vm身上成为vm的属性
4、当有人读取fullName时,get调用,返回值作为fullName的值
5、set什么时候调用? fullName被修改时调用,这里边呢有个连锁反应,我手动修改vm.fullName导致firstName和lastName被修改,Vue模板重新解析,页面刷新,firstName和lastName的修改又导致get被重新执行(依赖的数据变了),返回新的fullName,计算属性计算属性,就是一直在计算,所以要想改fullName,必须改它依赖的值才行。


<div id="root">
    姓: <input type="text" v-model="firstName"><br><br>
    名: <input type="text" v-model="lastName"><br><br>
    

    
    姓名: <span>{{fullName}}span><br><br>
    姓名: <span>{{fullName}}span><br><br>
    姓名: <span>{{fullName}}span><br><br>
    姓名: <span>{{fullName}}span><br><br>
    姓名: <span>{{fullName}}span><br><br>
    姓名: <span>{{fullName}}span>
div>

<script>
    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        computed: {
            //计算属性里面的属性要写成对象的形式,每个对象里面都有getter和setter
            //这个fullName实际上就是firstName和lastName经过一番计算得到的玩意儿,直接给到vm身上
            fullName: {
                //get有什么用?当有人读取fullName时,get调用,返回值作为fullName的值
                //get什么时候调用? 1.初次读取fullName  2.get里用到的数据发生了改变
                get() {
                    console.log('fullName被读了');
                    // console.log(this);//此处的this是vm
                    return this.firstName + '-' + this.lastName;
                },

                //set什么时候调用? fullName被手动修改时调用
                //这里边呢有个连锁反应,我手动修改vm.fullName导致firstName和lastName被修改,Vue模板重新解析
                //页面刷新,firstName和lastName的修改又导致get被重新执行(依赖的数据变了),返回新的fullName
                //计算属性计算属性,就是一直在计算,所以要想改fullName,必须改它依赖的值
                set(val) {
                    let arr = val.split('-');
                    this.firstName = arr[0]; 
                    this.lastName = arr[1];
                }
            }
        }
    })
script>

3. 计算属性总结

定义:要用的属性不存在,要通过Vue中已有属性计算得来。
原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
备注:
1.计算属性最终会出现在vm上,直接读取使用即可,不用加括号,和methods区别一下子。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变,这很关键。

4. 计算属性的简写

只考虑读取不考虑修改时可使用简写:


<div id="root">
    姓: <input type="text" v-model="firstName"><br><br>
    名: <input type="text" v-model="lastName"><br><br>
    

    
    姓名: <span>{{fullName}}span>
div>

<script>
    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        computed: {
            fullName() {
                console.log('fullName被读了');
                return this.firstName + '-' + this.lastName;
            }

        }
    })
script>

这里一定要注意,这个fullName是一个属性,不是一个方法,只不过是调用里面的getter,把返回值直接给了vm罢了,所以直接{{fullName}}就可以搞出来,不用加小括号调用!!

二、监视属性

1. 天气案例铺垫

实现点击按钮切换天气

  • 写法1:利用插值语法和三元表达式
  • 写法2:利用计算属性
<div id="root">
    
    
    <h2>今天天气很{{isHot ? '炎热': '寒冷'}}h2>

    
    <h2>今天天气很{{info}}h2>
    
    
    <button @click='change'>点击切换天气button>
div>
<script>
    new Vue({
        el: '#root',
        data: {
            isHot: true
        },
        methods: {
            change() {
                this.isHot = !this.isHot;
            }
        },
        computed: {
            info() {
                //注意这里的isHot要加this
                return this.isHot ? '炎热' : '寒冷';
            }
        },
    })  
script>

2. 引出监视属性

1、监视属性里有个handler函数,当被监视的属性(例如isHot)被修改时调用handler函数
2、handler函数传两个参数,分别是:(修改之后的值,修改之前的值)
3、监视的属性必须存在,才能进行监视!!既可以是data里的属性(isHot),也可以是计算属性(info)
4、监视的两种写法:

(1).new Vue时传入watch配置

<div id="root">
    
    
    <h2>今天天气很{{isHot ? '炎热': '寒冷'}}h2>

    
    <h2>今天天气很{{info}}h2>
    
    
    <button @click='change'>点击切换天气button>
div>
<script>
    new Vue({
        el: '#root',
        data: {
            isHot: true
        },
        methods: {
            change() {
                this.isHot = !this.isHot;
            }
        },
        computed: {
            info() {
                //注意这里的isHot要加this
                return this.isHot ? '炎热' : '寒冷';
            }
        },
        watch: {
            isHot: {
                immediate: true,   //初始化时先调用一次handler 
                //当isHot被修改时调用handler函数
                handler(newVal, oldVal) {
                    console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            },
            //watch不只可以监视data中的属性,还可以监视计算属性
            info: {
                immediate: true,   //初始化时先调用一次handler 
                //当isHot被修改时调用handler函数
                handler(newVal, oldVal) {
                    console.log('info被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            }
        }
    })  
script>

(2).通过vm.$watch监视

const vm = new Vue({
    el: '#root',
    data: {
        isHot: true
    },
    methods: {
        change() {
            this.isHot = !this.isHot;
        }
    },
    computed: {
        info() {
            //注意这里的isHot要加this
            return this.isHot ? '炎热' : '寒冷';
        }
    }
})

	vm.$watch('isHot', {
	    immediate: true,   //初始化时先调用一次handler 
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})
	
	vm.$watch('info', {
	    immediate: true,   //初始化时先调用一次handler 
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('info被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})

3. 深度监视

(1).Vue中的watch默认不监测对象内部值的改变(一层,如果监视那么对象地址不变就不变)。
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!想要可以就要手动开启deep:true
(2).使用watch时根据数据的具体结构,决定是否采用深度监视。

   
    <div id="root">
        <h3>a的值是:{{numbers.a}}h3>
        <button @click="numbers.a++">点我实现a+1button>
        <h3>b的值是:{{numbers.b}}h3>
        <button @click="numbers.b++">点我实现b+1button>
        <h3>我tm就非要强制让number变h3>
        <button @click="numbers = {a:666, b:999}">点我改变numbersbutton>
    div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                isHot: true,
                numbers: {
                    a: 1,
                    b: 2
                }
            },
            watch: {
                //监视多级结构中某个属性的变化,使用引号包起来(之前不加引号都是简写形式)
                'numbers.a': {
                    handler() {
                        console.log('a被改变了');
                    }
                },
                //如果下面这么写,即使ab变了也不会执行handler,因为这么写意思是监视numbers这个属性
                //而这个属性值是一个对象,只要numbers对象的地址不变就不变,除非像上面div写的暴力方法
                 numbers: {
                     handler() {
                         console.log('numbers改变了');
                     }
                 },

                //但是如果加个deep,就可以监视多级结构中某个属性的变化,ab变了numbers也变
                numbers: {
                    deep: true,
                    handler() {
                        console.log('numbers改变了');
                    }
                },
            }
        })
    script>

4.监视的简写形式

形式1:

	watch: {
                //复杂写法:
                 isHot: {
                    //immediate: true,   //初始化时先调用一次handler 
                    //当isHot被修改时调用handler函数
                    handler(newVal, oldVal) {
                        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                     }
                },
                
				//简写
                // 如果handler函数中没有其他的配置项(deep,immediate等),可以简写成以下形式
                isHot(newVal, oldVal) {
                    console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            }

形式2:

	//复杂形式
	vm.$watch('isHot', {
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})
	//简写形式
	vm.$watch('isHot',function (newVaule,oldVaule) {
	        console.log(newVaule,oldVaule);
	    })

三、watch对比computed

1.对比拼接姓名的案例


<div id="root">
    姓: <input type="text" v-model="firstName"><br><br>
    名: <input type="text" v-model="lastName"><br><br>
    
    姓名: <span>{{fullName}}span>
div>

计算属性:

const vm = new Vue({
    el: '#root',
    data: {
        firstName: '张',
        lastName: '三'
    },
    computed: {
        fullName() {
            console.log('fullName被读了');
            return this.firstName + '-' + this.lastName;
        }

    }
})

监视属性:

   const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三',
            fullName: '张-三'
        },
        watch: {
            firstName: {
                handler(newVal, oldVal) {
                    this.fullName = newVal + '-' + this.lastName;
                }
            },
            lastName: {
                handler(newVal, oldVal) {
                    this.fullName = this.firstName + '-' + newVal;
                }
            },
        }
    })

这么看起来好像计算属性更简单,那么计算属性就真的牛逼吗?我觉得watch更牛逼,因为它可以异步。
比如我如果手动改了firstName要等一秒钟再改fullName呢?
用监视属性,我们就可以这么写:

watch: {
    firstName: {
        handler(newVal, oldVal) {
            setTimeout(() => {
                this.fullName = newVal + '-' + this.lastName;
            }, 1000);
        }
    }
}

但是computed就没辙了,因为等一秒再return?这没法整啊这

2. 总结:

1、computed能完成的功能,watch都可以完成。
2、watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。

两个重要的小原则!
1、所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
2、所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

你可能感兴趣的:(Vue.js,vue.js,javascript,前端)