一、计算属性computed
1、定义:需要使用的属性不存在,用通过已有的属性,计算得来一个新的属性。
2、原理:底层接住了defineProperty()方法提供的getter和setter;
get函数调用时机:初次读取计算属性的时候执行一次;
当依赖的数据发生改变的时候也会执行;
3、优势:与methods相比:
1)computed具有缓存机制,多次调用只要依赖的数据不改变,直接读取缓存,效率更高;
2)调试更方便,开发者工具中能够清晰的看到data和computed分开展示,清晰可见。
4、备注:
1)计算属性最终也会出现在vm身上,模板中可以直接使用计算属性,像data中的数据一样。
2)如果计算属性要被修改,就必须写setter,用响应修改,且set中要引起计算属性依赖的属性发生变化。
// 通过 插值语法 实现姓名案例:
<div id="app">
姓: <input type="text" v-model="firstName"> <br/><br/>
名: <input type="text" v-model="lastName"> <br/><br/>
全名: <span>{{firstName}}-{{lastName}}</span>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
}
})
</script>
// 通过 methods 实现姓名案例:
<div id="app">
姓: <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/>// 执行一次
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
methods:{
fullName(){ // 调用多次
console.log('fullname')
// console.log(this) // this指向vm实例
return this.firstName + "-" + this.lastName
}
}
})
<script>
// 通过 computed 计算属性 实现姓名案例:
<div id="app">
姓: <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/> // 直接读取缓存
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed:{
fullName:{
// 把computed中的fullName中的get中的返回值放在vm身上,所以模板中直接读取
// get有什么作用??当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
// get什么时候调用:1.初次读取fullName时,2.所依赖的数据发生变化时(firstName和lastName)
get(){
// console.log(this) // this指向vm ,不能使用箭头函数
console.log('get被调用了')
// return '小猪佩奇'
return `${this.firstName}-${this.lastName}`
},
// set什么用??当有人修改fullName时,被调用
// set不一定必须写,一般计算属性只用于页面显示,就不用谢set了,一般情况只用get就可以,如果计算属性会被人修改,就必须写set(少数情况使用)
set(value){
console.log('set被调用')
this.firstName = value.split("-")[0]
this.lastName = value.split("-")[1]
}
}
}
})
</script>
//去掉注释后的代码:
<div id="app">
姓: <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/>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed:{
fullName:{
get(){
return `${this.firstName}-${this.lastName}`
},
set(value){
this.firstName = value.split("-")[0]
this.lastName = value.split("-")[1]
}
}
}
})
// 当只考虑读取计算属性,不考虑修改时,可以简写:
<div id="app">
姓: <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/>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed:{
fullName(){
return `${this.firstName}-${this.lastName}`
}
}
})
</script>
二、侦听属性watch
1、监视属性特点:
1)属性必须存在才能被监视。
2)可以监视data中的属性,也可以监视计算属性;
3)当监视的属性发生变化时,回调函数handler自动调用,执行相关代码,进行相关操作;
2、监视的两种方式:
1)new Vue()时,直接在配置对象中配置watch配置项;
2)通过vm.$watch监视;
3、深度监视:
1)vue提供的watch默认不监视对象内部值的改变(只监视一层);
2)配置deep:true可以开启深度监视,对对象内部进行深层监视(可监视多层);
3)vue自身是可以监视对象内部值的变化的,但vue提供的watch默认不可以;
4)使用watch时,根据数据的具体结构,决定是否采用深度监视;
4、实例部分
// 通过差值语法方法,实现天气切换实例:
<div id="app">
<h2>今天天气很{{isHot ? '炎热' : '凉爽'}}</h2>
//绑定事件的时候,@xxx = 'yyy', yyy可以写一些简单的语句
<button @click="isHot = !isHot">切换天气</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
isHot: true
}
})
</script>
// 通过计算属性方法,实现天气切换案例:
<div id="app">
<h2>今天天气很{{info}}</h2>
<button @click="changeWhether">切换天气</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
isHot: true
},
computed: {
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWhether(){
this.isHot = !this.isHot;
}
}
})
</script>
// 通过监听属性方法,实现天气切换案例:
<div id="app">
<h2>今天天气很{{info}}</h2>
<button @click="changeWhether">切换天气</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
isHot: true,
info: '炎热'
},
methods: {
changeWhether(){
this.isHot = !this.isHot;
}
},
// 写法一:
// watch: {
// isHot: {
// immediate: false, // 初始化是让handler调用一下;
// deep: false, // 深度侦听
// // handler什么时候调用?? 当isHot发生改变的时候;
// handler: function(newValue,oldValue){
// console.log('isHot被修改了',newValue,oldValue)
// this.info = this.isHot ? '炎热' : '凉爽'
// }
// }
// }
})
// 写法二:
vm.$watch('isHot',{
immediate: false, // 初始化是让handler调用一下;
deep: false, // 深度侦听
// handler什么时候调用?? 当isHot发生改变的时候;
handler: function(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
this.info = this.isHot ? '炎热' : '凉爽'
}
})
</script>
// 深度监视
<body>
<div id="app">
<h2>{{number.a}}</h2>
<button @click="number.a++">a++</button>
<h2>{{number.b}}</h2>
<button @click="number.b++">b++</button>
</div>
<script type="text/javascript" src="./vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
number:{
a:1,
b:2
}
},
watch: {
// 监视多级结构中某个属性的变化
// 'number.a': {
// handler: function(newValue,oldValue){
// console.log('number.a被修改了',newValue,oldValue)
// }
// },
// 'number.b': {
// handler: function(newValue,oldValue){
// console.log('number.b被修改了',newValue,oldValue)
// }
// }
// 监视多级结构中所有属性的变化:深度监视deep:true
number: {
deep: true, // 深度侦听
handler: function(newValue,oldValue){
console.log('number被修改了')
}
}
}
})
</script>
</body>
// 简写形式:
<div id="app">
<h2>今天天气和{{info}}</h2>
<button @click="changeWhether">切换天气</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
isHot: true
},
computed: {
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWhether(){
this.isHot = !this.isHot;
}
},
watch: {
// 正常写法:
// isHot: {
// // immediate:true,
// // deep: true, // 深度侦听
// handler: function(newValue,oldValue){
// console.log('number被修改了')
// }
// }
// 简写:
// isHot(newValue,oldValue){
// console.log('number被修改了',newValue,oldValue)
// }
}
})
// 正常写法:
vm.$watch('isHot',{
// immediate:true,
// deep: true, // 深度侦听
// handler: function(newValue,oldValue){
// console.log('number被修改了')
// }
})
// 简写:
vm.$watch('isHot',function(newValue,oldValue){
console.log('number被修改了',newValue,oldValue)
})
</script>
// 姓名案例watch和computed对比
// 根据具体情况选择合适的方法
// watch属性
<div id="app">
姓: <input type="text" v-model="firstName"><br><br>
名: <input type="text" v-model="lastName"><br><br>
全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三',
fullName: '张-三'
},
watch: {
firstName(newValue){
this.fullName = newValue + "-" + this.lastName;
},
lastName(newValue){
this.fullName = this.firstName + "-" + newValue;
}
}
})
</script>
// computed属性
<div id="app">
姓: <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/>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三'
},
computed:{
fullName(){
return `${this.firstName}-${this.lastName}`
}
}
})
</script>
三、计算属性computed和侦听属性watch对比
1、相同点
1)都是根据依赖变化从而发生变化;
2、区别
1)计算属性中,返回的是一个data中没有的值(新值)并且必须包含return;
2)侦听器变化的值是data中存在的值,并且不包含return, 在侦听器中可以执行异步操作,并控制操作的频率,这些都是计算属性无法做到的
3)computed能完成的功能,watch都可以完成;watch能完成的功能,computed不一定能完成;例如watch可以进行异步操作;
4)虽然计算属性在大多数情况下更合适。Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。 当需要在数据变化时执行异步或开销较大的操作时,watch这个方式是最有用的。
5)使用场景:
watch适合处理的场景是:侦听一个数的变化,当该数据变化,来处理其他与之相关数据的变化(该数据影响别的多个数据)
computed适合处理的场景是:获得一个值或者结果,该结果受其他的依赖的影响。(一个数据受多个数据影响)
3、两个重要的小原则:
1) 所有被Vue管理的函数最好写成普通函数,这样this的指向才是vm 或者 组件实例对象;
2) 所有不被Vue管理的函数(定时器的回调函数,ajax的回调函数,promise回调函数)最好写成箭头函数; 这样,this的指向才会指向vm或者组件实例对象;
<div id="app">
姓: <input type="text" v-model="firstName"><br><br>
名: <input type="text" v-model="lastName"><br><br>
全名: <span>{{fullName}}</span>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script>
Vue.config.productionTip = false; // 阻止Vue在启动时生成 生产提示
let vm = new Vue({
el: '#app',
data: {
firstName: '张',
lastName: '三',
fullName: '张-三'
},
watch: {
firstName(newValue){
setTimeout(()=>{ // 写成箭头函数,定时器是浏览器引擎的定时模块触发的,this指向window,箭头函数没有自己的this,会往外层找,找到的是vm,
// 计算属性中如果加定时,在定时中return返回的数据不在vm身上,return跑到window身上了
// 所以计算属性不能用定时等异步操作,watch不是返回,而是通this,直接改写vm身上的值,所以可以用于异步操作;
this.fullName = newValue + "-" + this.lastName;
},2000)
},
lastName(newValue){
this.fullName = this.firstName + "-" + newValue;
}
}
})
</script>