Vue 计算属性和监视属性详解细节 class类绑定和style样式绑定

Posted IT_Holmes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue 计算属性和监视属性详解细节 class类绑定和style样式绑定 相关的知识,希望对你有一定的参考价值。

1. Vue 两种实现语法(与计算属性相照应)

1.1 差值语法实现(不推荐)

源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>插值语法实现</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        姓:  <input type="text" v-model ="firstName"> <br>
        名:  <input type="text" v-model ="lastName"> <br>
        姓名: <span>{{firstName + "." +lastName}}</span> <br>
        <!-- slice()来截取内容 -->
        姓名: <span>{{firstName.slice(0,3)}}-{{lastName}}</span>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                firstName:"张",
                lastName:"三"
            }
        });
        vm.$mount("#root");
    </script>    
</body>
</html>

像下面这样的格式虽然不会报错,但并不推荐使用:

 <span>{{firstName.slice(0,3)}}-{{lastName}}</span>

1.2 methods实现 (推荐)

使用methods来实现,相同的功能。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>methods</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        姓: <input type="text" v-model="firstName"> <br>
        名: <input type="text" v-model="lastName"> <br>
        <!-- fullName不写(),相当于把函数放到了这里。 -->
        姓名: <span>{{fullName}}</span> <br>
        <!-- 加了fullName()相当于把它的返回值放到这里。 -->
        姓名: <span>{{fullName()}}</span> <br>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{
                firstName:"张",
                lastName:"三"
            },methods:{
                fullName(){
                    const temp = this.firstName.slice(0,3) + "-" +this.lastName
                    return temp;
                }
            }
        });
    </script>
</body>
</html>

注意:
只要Vue中的data值,发生改变它就要重新解析模板。

2.Vue 计算属性 (推荐使用)

2.1 计算属性 computed get函数

在Vue中,属性就是data中的数据。

而计算属性呢,则是在computed中。compute也是在Vue实例查到,因此我们可以直接使用差值语法来嗲用fullName就行了。

案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算属性</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        姓: <input type="text" v-model="firstName"> <br>
        名: <input type="text" v-model="lastName"> <br>
        姓名: <span>{{fullName}}</span>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{
                firstName:"张",
                lastName:"三"
            },
            //compute也是在Vue实例查到,因此我们可以直接使用差值语法来嗲用fullName就行了。
            computed:{
                fullName:{
                    //get作用,当有人读取fullName时,get就会被调用,且返回值就是作为fullName的值。
                    get(){
                        console.log('get');
                        console.log(this); //这里的this也是指向的Vue实例对象
                         return this.firstName + "-" + this.lastName;
                    }
                }
            }

        });
    </script>
</body>
</html>

上面因为缓存内容就会考虑什么时候调用get,get什么时候调用?

1. 初次读取fullName时,get会被调用。(这里涉及了页面缓存的问题。)
2. 所依赖的数据发生变化时,也就是data的内容发生变化,自然会重新调用computed中的函数。。

这里关于fullName中的get有一个误区一定注意:

2.2 计算属性 computed set函数

有get,就会有set方法,什么时候会用到set?

当computed中的属性被修改时,就要用到set。

案例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算属性</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        姓: <input type="text" v-model="firstName"> <br>
        名: <input type="text" v-model="lastName"> <br>
        姓名: <span>{{fullName}}</span>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            el:"#root",
            data:{
                firstName:"张",
                lastName:"三"
            },
            //compute也是在Vue实例查到,因此我们可以直接使用差值语法来嗲用fullName就行了。
            computed:{
                fullName:{
                    //get作用,当有人读取fullName时,get就会被调用,且返回值就是作为fullName的值。
                    get(){
                        console.log('get');
                        // console.log(this); //这里的this也是指向的Vue实例对象
                         return this.firstName + "-" +this.lastName;
                    },
                    set(value){
                        console.log("调用了set方法:",value);
                        const arr = value.split('-');
                        this.firstName = arr[0];
                        this.lastName = arr[1];
                    }
                }
            }

        });
    </script>
</body>
</html>


注意:被Vue管理的函数,不要用箭头函数,会改变this。

总结:

2.3 getter的简写(前提不考虑修改,配置setter时)

3. Vue 监视属性

3.1 注意事项

首先,介绍一个插件:


Vue开发者工具存在一个问题,就是如果没有牵动改变页面方面的内容,开发者工具中的root里面的data值一般不会变化,实际上是变化了。

源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 插值语法要写表达式,不能用if,因为if不是表达式。 因此可以用三元表达式-->
        <h1>今天天气很{{info}}</h1>
        <button @click="chengeWeather">切换天气</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                isHot:true
            },
            methods: {
                chengeWeather(){
                    this.isHot = !this.isHot; //取反之后的isHot。
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热":"凉爽"
                }
            }
        });
        vm.$mount("#root");
    </script>
</body>
</html>

对于上面这种我们还可以更加简化一些,我们直接在@click中写判断式也是可以的,如下:

<button @click="isHot = !isHot">切换天气</button>

也就是说div模板里面的东西是可以直接调用Vue实例对象中的内容。如果有两个以上的语句是可以通过" ;"来分割的。不过不推荐,还是在methods对象中定义比较好。

源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 插值语法要写表达式,不能用if,因为if不是表达式。 因此可以用三元表达式-->
        <h1>今天天气很{{info}}</h1>
        <button @click="isHot = !isHot">切换天气</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                isHot:true
            },
            methods: {
                
            },
            computed:{
                info(){
                    return this.isHot ? "炎热":"凉爽";
                }
            }
        });
        vm.$mount("#root");
    </script>
</body>
</html>

如果我们像用Vue实例对象中没有的事件,例如alert,我们可以在data中导入:


当然,像这种在事件(@xxx = “yyy”)内写的方式不推荐,只是适合比较简单的内容,内容多了就非常冗余。

3.2 监视属性

3.2.1 监视属性watch 写法一

引入新的对象,watch就是用来监视的。

hander()函数: 当被监视的对象发生改变时,它就会被调用。

hander(newValue,oldValue)函数有两个参数,分别是修改前和修改后的值。

与hander对象函数对应的有immediate对象属性,作用就是初始化时,让handler调用一下。

源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 插值语法要写表达式,不能用if,因为if不是表达式。 因此可以用三元表达式-->
        <h1>今天天气很{{info}}</h1>
        <button @click="changeWeather">切换天气</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                isHot:true
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot;
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热":"凉爽"
                }
            },
            //watch就是用来监视的。
            watch:{
                //指定监视isHot,并且在对象中配置监视内容。
                isHot:{
                    //hander()函数很重要,当isHot发生改变时,它就会被调用。
                    //hander(newValue,oldValue)函数有两个参数,分别是修改前和修改后的值。
                    immediate:false, //初始化时,让handler调用一下。 
                    handler(newValue,oldValue){
                        console.log("isHot被修改了",newValue,oldValue);
                    }
                },
                //computed中的对象属性也是可以确定的。
                info:{
                    immediate:true, 
                    handler(newValue,oldValue){
                        console.log("isHot被修改了",newValue,oldValue);
                    }
                }
            }
        });
        vm.$mount("#root");
    </script>
</body>
</html>

3.2.2 监视属性watch 写法二

这里提醒一下,曾经我们对象和值的写法是这样子的,’ key ’ : ’ value ',后来呢只不过默认可以写成了key : ’ value ’ ,这里记一下要。

我们可以使用$watch()来在Vue实例对象外面,来定义监听。

源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 插值语法要写表达式,不能用if,因为if不是表达式。 因此可以用三元表达式-->
        <h1>今天天气很{{info}}</h1>
        <button @click="changeWeather">切换天气</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                isHot:true
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot;
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热":"凉爽"
                }
            }
        });
        vm.$mount("#root");
        vm.$watch('isHot',{
            immediate:false,
            handler(newValue,oldValue){
                console.log("监听isHot。",newValue,oldValue);
            }
        })
    </script>
</body>
</html>

对于这两种写法,第二种写法比较适合在不确定监测哪一个属性时适用,第一种写法比较适合已经确定好了监听那一个属性。

3.3 深度监视属性

因为在对象中键值的key,必须是字符串形式,所以可以写成’ key.xxx ‘:’ value '的样式。
专业术语上是:监视多级结构中某个属性的变化,用下面的样式。

源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <!-- 插值语法要写表达式,不能用if,因为if不是表达式。 因此可以用三元表达式-->
        <h1>今天天气很{{info}}</h1>
        <button @click="changeWeather">切换天气</button>
        <hr>
        <h3>a的值是:{{numbers.a}}</h3>
        <button @click="numbers.a++">点我让a加1</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                isHot:true,
                numbers:{
                    //在这里我们只监视a,不监视b,从而达到一个深度监视的效果。
                    a:1,
                    b:1
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot;
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热":"凉爽"
                }
            },
            watch:{
                isHot:true,
                //因为这里的键值的key,必须是字符串形式,所以可以写成下面的样式。
                //专业术语上是:监视多级结构中某个属性的变化,用下面的样式。
                'numbers.a'(){
                    console.log(this.numbers.a);
                }
            }
        });
        vm.$mount("#root");
    </script>
</body>
</html>

当然,如果有上百个需要监视的对象属性,怎么办?
首先,就是我们不能直接监视numbers本省。因为我们的目标是监视numbers里面的内容,再次就是会发生下图的情况,就算numbers中的a、b发生变化,numbers中的handler也不会执行。
如何解决,这里我们可以配置deep:true来解决。
源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <h1>今天天气很{{info}}</h1>
        <button @click="changeWeather">切换天气</button>
        <h3>a的值是:{{numbers.a}}</h3>
        <button @click="numbers.a++">点我让a加1</button>
        <h3>b的值是:{{numbers.b}}</h3>
        <button @click="numbers.b++">点我让b加1</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                isHot:true,
                numbers:{
                    a:1,
                    b:1
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot;
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热":"凉爽"
                }
            },
            watch:{
                isHot:true,
                numbers:{
                    //deep默认值是false,配置deep:true可以检测对象内部值发生改变。
                    deep:true,
                    handler(){
                        console.log("number内部发生变化了。")
                    }
                }
                
            }
        });
        vm.$mount("#root");
    </script>
</body>
</html>

总结:

3.5 监视的简写形式

在没有immediate,deep等其他的对象属性,只有handler时可以直接简写形式。

源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
</head>
<body>
    <div id="root">
        <h1>今天天气很{{info}}</h1>
        <button @click="changeWeather">切换天气</button>
        <h3>a的值是:{{numbers.a}}</h3>
        <button @click="numbers.a++">点我让a加1</button>
        <h3>b的值是:{{numbers.b}}</h3>
        <button @click="numbers.b++">点我让b加1</button>
    </div>
    <script>
        Vue.config.productionTip = false;
        const vm = new Vue({
            data:{
                isHot:true,
                numbers:{
                    a:1,
                    b:1
                }
            },
            methods: {
                changeWeather(){
                    this.isHot = !this.isHot;
                }
            },
            computed:{
                info(){
                    return this.isHot ? "炎热":"凉爽"
                }
            },
            watch:{
                // isHot:{
                //     immediate:true,
                //     deep:true,
                //     handler(newValue,oldValue){
                //         console.log('isHost被修改',newValue,oldValue)
                //     }
                // }
                //在没有immediate,deep等其他的对象属性,只有handler可以简写为下面样式:
                isHot(newValue,oldValue){
                    console.log("isHost被修改",newValue,oldValue);
                }
            }
        })
        vm.$mount("#root");
    </script>
</body>
</html>

同样,$watch也是可以简写的,但不能有其他配置对象属性,只能有handler()函数。

4. watch和computed比较

其实,watch和computed区别并不是很大,都能够实现,但computed因为必须有return返回值这一说,所以实现起来,会有一定阻碍。

在watch中定义,像setTimeout这样的全局函数,一定要写箭头函数,多注意this的情况。看下图:


监视属性,现在在官方文档里面叫做侦听属性。

5. Vue绑定 class类

正常的样式正常写就可以,需要通过Vue来改变的样式可以用 :class=“xxx”,这样的方式来写。

5.1 字符串写法

写法一:绑定class样式,字符串写法。
适用于:样式的类名不确定,需要动态指定.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <title>Document</title>
    <style>
        .demo1{
            width: 400px;
            height: 300px;
            color: salmon;
        }

        .demo2{
            border: 2px solid black;
        }

        .demo3{
            border: 10px solid red;
        }
        
        .demo4{
            border: 20px solid green;
        }
    </style>
</head>
<body>
    <div id="root">
        <!-- 绑定class样式,字符串写法,适用于:样式的类名不确定,需要动态指定 -->
        <div class="demo1" :class="mood" @click="changeMood">{{name}}</div>
    </div>
    
    <script>
        Vue.config.productionTip = false;
        
        const vm = new Vue({
            el:"#root",
            data:{
                name:'夏洛克',
                mood:'demo2'
            },
            methods:{
                changeMood(){
                    const arr = ['demo2','demo3','demo4'];
                    const index = Math.floor(Math.random()*3);
                    this.mood = arr[index];
                }
            }
        
        });
    </script>
</body>
</html>

5.2 数组写法

写法二:绑定class样式,数组写法。
适用于:要绑定的样式个数不确定,名字也不确定。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <title>Document</title>
    <style>
        .demo1{
            width: 400px;
            height: 300px;
            color: salmon;
        }

        .demo2{
            border: 2px solid black;
        }

        .demo3{
            border: 10px solid red;
        }
        
        .demo4{
            border: 20px solid green;
        }

        .backg{
            color: grey;
        }

        .border{
            border-radius: 50%;
        }
    </style>
</head>
<body>
    <div id="root">
        <!-- 当然对于应用多个样式,可以直接在class上面操作就可以。 -->
        <!-- 如果应用多个样式也可以写成一个数组形式,当然重要的是对它进行修改删除等操作。 -->
        <div class="demo1" :class="arr">{{name}}</div>
    </div>
    
    <script>
        Vue.config.productionTip = false;
        
        const vm = new Vue({
            el:"#root",
            data:{
                name:'夏洛克',
                mood:'demo2',
                arr:['backg','border','demo3']
            }
        
        });
    </script>
</body>
</html>

我们可以操作一些增删改查:

注意事项:

5.3 对象写法

绑定class样式,对象写法,适用于:要绑定的样式个数确定,名字也确定,但要动态决定用不用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <title>Document</title>
    <style>
        .demo1{
            width: 400px;
            height: 300px;
            color: salmon;
        }

        .demo2{
            border: 2px solid black;
        }

        .demo3{
            border: 10px solid red;
        }
        
        .demo4{
            border: 20px solid green;
        }

        .backg{
            color: grey;
        }

        .border{
            border-radius: 50%;
        }
    </style>
</head>
<body>
    <div id="root">
        <div class="demo1" :class="obj">{{name}}</div>
    </div>
    
    <script>
        Vue.config.productionTip = false;
        
        const vm = new Vue({
            el:"#root",
            data:{
                name:'夏洛克',
                mood:'demo2',
                obj:{
                    demo2:true,
                    demo4:false,
                    border:true
                }
            }
        
        });
    </script>
</body>
</html>

6. Vue绑定 style样式

style样式,也是包括对象写法和数组写法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <div id="root">
        <!-- 对象写法 -->
        <div :style="styleObj">{{name}}</div>

        <!-- 数组写法 -->
        <div :style="[styleObj,styleObj2]">{{name}}</div>
        <div :style="styleArr">{{name}}</div>
    </div>
    
    <script>
        Vue.config.productionTip = false;
        
        const vm = new Vue({
            el:"#root",
            data:{
                name:'夏洛克',
                mood:'demo2',
                styleObj:{
                    fontSize:'50px',   //font-size这里设置成了fontSize。
                    color:'red' 
                },
                styleObj2:{
                    backgroundColor:'orange'
                },
                styleArr: [
                    {     //用数组形式来表现
                        fontSize: '50px',
                        color: 'red'
                    },
                    {
                        backgroundColor: 'orange'
                    }
                ]          
            }
        
        });
    </script>
</body>
</html>


样式对象理解:

以上是关于Vue 计算属性和监视属性详解细节 class类绑定和style样式绑定 的主要内容,如果未能解决你的问题,请参考以下文章

使用计算属性 Vue 2 的条件类绑定

JS,Vue2,事件处理,计算属性,监视属性,class与style绑定

Vue 中的计算属性不会触发监视

Vue监听属性详解

Vue监听属性详解

Vue监听属性详解