传统方式做运算
方式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、lastNamereturn 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是vmreturn 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是vmreturn this.firstName + "-" + this.lastName;},// setter非必须,只有fullName会被修改时才需要该方法set(value) {console.log('setter方法执行了:' + value);}}}});
当计算属性只有getter时,可以进行简写:
computed: {// fullName属性只有getter,可以直接简写成fullName(),表示就是该属性的getterfullName() {console.log('fullName被读取了');console.log(this); // 此处的this是vmreturn 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的回调函数,不能返回给getsetTimeout(() => {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或组件实例对象。
