传统方式做运算
方式1:直接在插值表达式里面做运算
<div id="app">
姓:<input type="text" v-model="firstName">
<br>
名:<input type="text" v-model="lastName">
<br>
全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
</div>
</body>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
}
});
</script>
Vue不推荐在插值表达式里加入过多的运算逻辑。
方式2:计算逻辑配置成method,然后将method配置到插值表达式位置调用
<body>
<div>
methods实现
</div>
<div id="app">
姓:<input type="text" v-model="firstName">
<br>
名:<input type="text" v-model="lastName">
<br>
<!-- 在插值表达式里调用method -->
全名:<span>{{fullName()}}</span>
<br>
</div>
</body>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
methods: {
fullName() {
// 因为method的this和data的this都是指的Vue实例vm,所以此处可以获取到vm的firstName、lastName
return this.firstName + "-" + this.lastName;
}
},
});
</script>
计算属性
<body>
<div>
计算属性实现
</div>
<div id="app">
姓:<input type="text" v-model="firstName">
<br>
名:<input type="text" v-model="lastName">
<br>
全名:<span>{{fullName}}</span>
<br>
第二次读取全名:<span>{{fullName}}</span>
<br>
</div>
</body>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
// 计算属性
computed: {
fullName: {
get() { // 计算属性需要有个getter,在该方法中编写计算逻辑
console.log('fullName被读取了');
console.log(this); // 此处的this是vm
return this.firstName + "-" + this.lastName;
}
}
}
});
</script>
计算属性是计算出来的,在vm._data
中不存在。
计算属性在通过getter获取完值之后会缓存下来,如果内容不变,下次再读取时将不再进行计算。但是当其依赖的变量发生变化时,计算属性的getter会再次被调用。
计算属性如果只是为了读取,那么只编写getter即可。但是如果需要修改计算属性,那么还需要编写setter:
(一般情况下,计算属性是计算完用来读取的,很少有修改计算属性的)
const vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
// 计算属性
computed: {
fullName: {
get() {
console.log('fullName被读取了');
console.log(this); // 此处的this是vm
return this.firstName + "-" + this.lastName;
},
// setter非必须,只有fullName会被修改时才需要该方法
set(value) {
console.log('setter方法执行了:' + value);
}
}
}
});
当计算属性只有getter时,可以进行简写:
computed: {
// fullName属性只有getter,可以直接简写成fullName(),表示就是该属性的getter
fullName() {
console.log('fullName被读取了');
console.log(this); // 此处的this是vm
return this.firstName + "-" + this.lastName;
}
}
监视属性(侦听属性)
- 通过
vm
对象的$watch()
或watch配置来监视指定的属性 - 当属性变化时,回调函数自动调用,在函数内部进行计算
@click
等事件绑定方法可以直接编写一些简单的实现:
<body>
<div id="app">
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#app',
data: {
isHot: true
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
}
},
})
</script>
可以简写为:
<!-- 直接在绑定事件上编写简单的语句 -->
<button @click="isHot = !isHot">切换天气</button>
此时,如果想监控isHot
属性的修改,可以使用watch
配置监视:
const vm = new Vue({
el: '#app',
data: {
isHot: true
},
// .......
// 配置监视
watch: {
isHot: { // 要监视的属性
handler(newValue, oldValue) { // 当isHot发生改变时被调用
console.log(`isHot改变了,从${oldValue}变成了${newValue}`);
}
}
}
})
其他配置项:
// 配置监视
watch: {
isHot: { // 要监视的属性
handler(newValue, oldValue) { // 当isHot发生改变时被调用
console.log(`isHot改变了,从${oldValue}到${newValue}`);
},
immediate: true // 页面打开就直接先执行一次handler方法,默认false
}
}
计算属性也是可以被watch
监测的。
通过vm
也可以配置监视:
const vm = new Vue({
el: '#app',
data: {
isHot: true
},
computed: {
weather() {
return this.isHot ? "炎热" : "凉爽"
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
}
}
});
// 通过vm的$watch方法进行监视
// 参数1,要监视的属性
// 参数2,配置项
vm.$watch('isHot', {
handler(newValue, oldValue) { // 当isHot发生改变时被调用
console.log(`isHot改变了,从${oldValue}到${newValue}`);
},
immediate: true
});
将被监视的属性变化时,回调函数自动调用,进行相关操作。
监视的属性必须存在,才能进行监视。
vm.$watch()
方式监视的属性不存在时不报错
监视多级结构中某个属性的变化:
const vm = new Vue({
el: '#app',
data: {
isHot: true,
numbers: {
a: 1,
b: 1
}
},
watch: {
// 监视多级结构中某个属性的变化
'numbers.a': { // 监视 numbers里面的a属性的变化
handler(newValue, oldValue) {
console.log('a被改变了')
}
}
}
});
监视对象的里面的属性变化:
const vm = new Vue({
el: '#app',
data: {
isHot: true,
numbers: {
a: 1,
b: 1
}
},
// 配置监视
watch: {
numbers: {
handler() {
console.log('numbers改变了');
},
// 开启深度监视,默认false
// 如果不开启深度监视,只有numbers这个对象本身变化了才能监视到。numbers里面的a、b属性值变化无法检测到
// 开启了深度监视后,numbers里面的属性值变化也可以被监视到
deep:true
}
}
});
监视属性简写:
当配置项里只有handler()
时,就可以使用简写形式
watch: {
isHot(newValue, oldValue) {
console.log(`isHot改变了,从${oldValue}到${newValue}`);
}
}
vm.$watch
形式监视的简写:
vm.$watch('isHot', function (newValue, oldValue) {
console.log(`isHot改变了,从${oldValue}到${newValue}`);
});
计算属性和监视属性对比
监视属性可以开启异步。
例如:
监视属性可以使用以下写法:
const vm = new Vue({
el: '#app',
data: {
firstName:'张',
lastName:'三',
fullName:'张-三'
},
watch: {
firstName(newValue) {
// firstName修改后,延迟1秒再修改fullName的值
setTimeout(() => {
this.fullName = newValue + "-" + this.lastName;
}, 1000);
}
}
});
计算属性因为是return,所以不能开启异步:
const vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName: {
get() {
// 此处这么写是错误的,运行不出来的
// 此处的return 会将返回值返回给setTimeout的回调函数,不能返回给get
setTimeout(() => {
return this.firstName + "-" + this.lastName;
}, 1000);
}
}
}
});
还需要注意的是:在监视属性中,使用setTimeout
延迟时,里面的回调函数必须写成箭头函数的形式:
watch: {
firstName(newValue) {
// 此处的setTimeout里面回调函数必须写成箭头函数
setTimeout(() => {
this.fullName = newValue + "-" + this.lastName;
}, 1000);
}
}
因为setTimeout
里面的回调函数不是VUE管理的函数,而是js引擎管理的。所以如果里面不写成箭头函数,那么里面的回调函数的this
就将指向window
。里面的回调函数写成箭头函数后,箭头函数没有this
,向外找的时候就会找到vue实例的this。
总结:
所有被Vue管理的函数,最好写成普通函数,这样this指向的才是vm或组件实例对象。
不是vue管理的函数(例如定时器的回调函数、ajax的回调函数、promise的回调函数),最好写成箭头函数,这样this指向的才是vm或组件实例对象。