本文目录:
- 1.什么是计算属性
- 2.计算属性的作用
- 3.computed里面的getter和setter
- 4.watch使用的几种方法
- 5.详解watch中的immediate和deep属性
- 6.表单监听
- 7.总结
1.什么是计算属性
计算属性从字面上理解,它类似属性的用法,但是却是可以计算的,通俗的角度上方法的调用是这样的:obj.fn(),属性的调用是这样的:obj.name, 而计算属性从内容上可能长得像方法,但使用的时候,不加括号就可以调用,我们来看下面的例子:
<p>{{computedMessage}}</p>
......
computed: {
computedMessage(){
return this.message.split('').reverse().join('')
}
}
2.计算属性的作用
1.使用计算属性,可以让模版中少用一些逻辑计算,便于维护
如果没有计算属性,模版中的代码是这样的
<p>{{message.split('').reverse().join('')}}</p>
有了计算属性,模版中的代码是这样的
<p>{{computedMessage}}</p>
2.计算属性可以缓存,提高性能
计算属性长得和方法一样,那是不是意味着我们可以直接把逻辑写在方法里面,然后直接调用方法就可以了呢?
<div id="app">
<p>计算属性运行结果:{{computedMessage}}</p>
<p>方法运行结果:{{fnMessage()}}</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
message:'hello nodeing!!!!'
},
methods:{
fnMessage(){
//split将message分割成数组,然后反转,然后使用join给连接成一个新的字符串
return this.message.split('').reverse().join('')
}
},
computed: {
computedMessage(){
return this.message.split('').reverse().join('')
}
}
});
</script>
从结果来看,它们的结果的确是一样的,但是它们也有区别,那就是计算属性是基于它们的依赖进行缓存的,而方法是需要每次去计算的,上面的代码中,fnMessage多次被调用,都会去执行内部的代码,而对于计算属性来说,它是基于它们的依赖进行缓存,意思就是说计算属性会缓存它计算出来的值,只要它的依赖没有变化,那么它每次取的值就是缓存起来的结果,而并不会再次去运算,这样就节省了调用的计算开销。在上面代码中,计算属性是依赖message的,只要message不变,计算属性不会再次计算,只有message变化了,计算属性才会再次计算
根据上面的结论,那么下面这种代码结果是不会变化的
computed: {
computedMessage(){
return Date.now()
}
}
上面代码中,计算属性并不依赖于某一个属性,所以多次被调用并不会改变其结果
计算属性是根据其依赖的变量的变化而自动变化
下面我们来看一个具体的例子,
<div id="app">
<button @click="fn1">计算属性</button>
<p v-if="isShow">{{computedMessage}}</p>
<br><br>
<button @click="fn2">方法</button>
<p v-if="isFnShow">{{fnMessage()}}</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
isShow: false,
isFnShow: false
},
methods: {
fnMessage() {
let date = new Date();
return date.getMilliseconds() //每次重新渲染,都会重新调用一下方法
},
fn1(){
this.isShow = !this.isShow
},
fn2() {
this.isFnShow = !this.isFnShow
}
},
computed: {
computedMessage() {
let date = new Date();
return date.getMilliseconds() //计算一次获得的值会被缓存起来
}
}
});
</script>
3.computed里面的getter和setter
在 Vue 中,computed 的属性可以被视为是 data 一样,可以读取和设值,因此在 computed 中可以分成 getter(读取) 和 setter(设值),一般情况下是没有 setter 的,computed 预设只有 getter ,也就是只能读取,不能改变设值。
vue.js计算属性默认只有 getter,因为是默认值所以我们也常常省略不写,如下代码:
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
其实computed里的代码完整的写法应该是:
computed: {
fullName: {
get(){
return this.firstName + ' ' + this.lastName
}
}
}
计算属性getter的触发时间:
<template>
<div id="demo">
<p> {{ fullName }} </p>
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
</div>
</template>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'zhang',
lastName: 'san'
},
computed: {
fullName: function () {
console.log('computed getter...')
return this.firstName + ' ' + this.lastName
}
},
updated () {
console.log('updated')
}
})
如果我们改变上边代码里的2个输入框的值firstName或者lastName,都会触发computed以及updated (),也就是说会执行: console.log('computed getter...')和console.log('updated') (用来验证是不是执行了,没有其他意思)
需要注意的是,不是说我们更改了getter里使用的变量,就会触发computed的更新,前提是computed里的值必须要在模板里使用才行。怎么理解呢?
如下代码,我们把template里使用到fullName 变量p标签注释掉:
``
就算我们更改了firstName以及lastName都不会触发computed 中的 getter 中的console.log('computed getter...'),而只会触发console.log('updated'),这就代表computed里面的属性必须要动态渲染到页面上才会自动触发更新。
计算属性settter:
<div id="demo">
<p> {{ fullName }} </p>
<input type="text" v-model="fullName">
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
</div>
......
computed: {
fullName: {
//getter 方法
get() {
console.log('computed getter...')
return this.firstName + ' ' + this.lastName
},
//setter 方法
set(newValue) {
console.log('computed setter...')
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
return this.firstName + ' ' + this.lastName
}
}
},
在template 中,我们可以看到,input 是直接绑 v-model="fullName",如果我们这里直接修改了fullName的值,那么就会触发setter,同时也会触发getter以及updated函数。其执行顺序是setter -> getter -> updated,如下:
console.log('computed setter...')
console.log('computed getter...')
console.log('updated')
这里需要注意的是,并不是触发了setter也就会触发getter,他们两个是相互独立的。我们这里修改了fullName会触发getter是因为setter函数里有改变firstName 和 lastName 值的代码。也就是说我们如果注释掉上边的setter中修改firstName 和lastName的代码后就不会执行getter,如下:
set(newValue){
console.log('computed setter...')
// var names = newValue.split(' ')
// this.firstName = names[0]
// this.lastName = names[names.length - 1]
return this.firstName + ' ' + this.lastName
}
代码依旧会执正常运行,且顺序如下
console.log('computed setter...')
console.log('updated')
watch属性可以用来监听每一个属性的变化,watch这个对象里面都是函数,函数的名称是data中的属性名称,watch中的函数不需要调用,当属性发生改变那么就会触发watch函数,每个函数都会接受两个值,一个是新值,一个是旧值,我们可以在watch当中就行新旧值的判断来减少虚拟dom的渲染。
4.watch使用的几种方法
(1)通过watch监听data数据的变化,数据发生变化时,就会打印当前的值
data(newVal, oldVal) {
console.log(newVal)
console.log(oldVal)
}
}
(2)通过watch监听docData数据的变化,数据发生变化时,this.change_number++(使用深度监听)
watch: {
docData: {
handler(newVal) {
this.change_number++
},
deep: true
}
}
(3)通过watch监听data数据的变化,数据发生变化时,执行changeData方法
watch: {
data: 'changeData' // 值可以为methods的方法名
},
methods: {
changeData(curVal,oldVal){
conosle.log(curVal,oldVal)
}
}
5.详解watch中的immediate和deep属性
(1)immediate
其值是true或false;确认是否以当前的初始值执行handler的函数
不使用immediate属性时的watch监听变量时有一个特点,就是当值第一次绑定时,不会执行监听函数,只有值发生改变时才会执行。如果我们需要在最初绑定值的时候也执行函数,则就需要用到immediate属性。
watch: {
docData: {
handler(newVal) {
this.change_number++
},
immediate: true
}
}
(2)deep
其值是true或false;确认是否深入监听。(一般监听时是不能监听到对象属性值的变化的,数组的值变化可以监听到。)
当需要监听一个对象的改变时,普通的watch方法只是监听该对象的引用,无法监听到对象内部属性的改变,此时就需要deep属性对对象进行深度监听。
data() {
return {
docData: {
'doc_id': 1,
'tpl_data': 'abc'
}
}
},
watch: {
docData: {
handler(newVal) {
this.change_number++
},
deep: true
}
}
设置deep:true则可以监听到docData.doc_id的变化,此时会给docData的所有属性都加上这个监听器,当对象属性较多时,每个属性值的变化都会执行handler。如果只需要监听对象中的一个属性值,则可以做以下优化:使用字符串的形式监听对象属性:
data() {
return {
docData: {
'doc_id': 1,
'tpl_data': 'abc'
}
}
},
watch: {
'docData.doc_id': {
handler(newVal, oldVal) {
......
}
}
}
这样只会给对象的某个特定的属性加监听器。
或者也可以使用计算属性 computed 作为中间层,如下:
computed: {
age() {
return this.obj.age
}
},
watch: {
age(newValue, oldValue) {
console.log(newValue)
}
}
(3)handler
其值是一个回调函数。即监听到变化时应该执行的函数。
6.表单监听
在开发中把表单动态绑定的所有数据都存放到一个对象,这时候可以利用watch监听一个表单中的所有属性的变化(即对象中任何一个属性发生变化时,便会触发该对象的监听,利用深度监听),从而去做一些逻辑处理,比如:当表单中的所有值都不为空的时候去执行相应的逻辑。
watch: {
obj: {
handler (newValue, oldValue) {
if (obj.a && obj.e && obj.d && obj.y && obj.w) {
// 当表单中的所有值都不为空的时候执行的函数
} else {
// 当表单中的有值为空的时候执行的函数
}
},
deep: true
}
}
7.总结
数组(一维、多维)的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。
注意,不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.updateAutocomplete 将是 undefined。
watch 与 computed的区别:
watch:watch用于观察和监听页面上的vue实例,当你需要在数据变化响应时,执行异步操作,或高性能消耗的操作,那么watch为最佳选择。
computed :可以关联多个实时计算的对象,当这些对象中的其中一个改变时都会触发这个属性具有缓存能力,所以只有当数据再次改变时才会重新渲染,否则就会直接拿取缓存中的数据。