1 计算属性
1.1 计算属性的引入
组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性或方法。复杂表达式会让你的模板变得不那么声明式。我们应该尽量描述应该出现的是什么,而非如何计算那个值。而且计算属性和方法使得代码可以重用。
<body>
<div id="name-methods">
姓:<input type="text" v-model="firstName"><br/>
名:<input type="text" v-model="secondName"><br/>
全名:<span>{{firstName.slice(0,3)}}-{{secondName}} </span>
</div>
<script type="text/javascript">
new Vue({
el: '#name-methods',
data() {
return {
firstName: 'zhao',
secondName: 'shuai-lc'
}
},
methods: {}
})
</script>
</body>
</html>
抽象成如下方式更为合适:
<body>
<div id="name-methods">
姓:<input type="text" v-model="firstName"><br/>
名:<input type="text" v-model="secondName"><br/>
全名:<span>{{ fullName() }}</span>
</div>
<script type="text/javascript">
new Vue({
el: '#name-methods',
data() {
return {
firstName: 'zhao',
secondName: 'shuai-lc@inspur.com'
}
},
methods: {
fullName() {
return this.firstName + '-' + this.secondName
}
}
})
</script>
</body>
</html>
1.2 计算属性的理解
-
定义:要用的属性不存在,要通过已有的属性计算得来。
-
原理:底层借助了Object.defineproperty方法提供的getter和setter。
-
get函数什么时候执行?
1)初次读取时会执行一次。
2)当依赖的数据发生改变时会被再次调用。 -
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
-
备注:
1)计算属性最终会出现在vm上,直接读取使用即可。
2)如果计算属性要被修改,那必须直接写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<body>
<div id="name-calculate">
姓:<input type="text" v-model="firstName"><br/>
名:<input type="text" v-model="secondName"><br/>
全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#name-calculate',
data: {
firstName: 'zhao',
secondName: 'shuai-lc@inspur.com'
},
methods: {},
computed: {
fullName: {
// 当有人读取fullName的时候调用get()
// 1)初次读取时会执行一次。
// 2)当依赖的数据发生改变时会被再次调用。
get() {
return this.firstName + ' - ' + this.secondName
},
// 当有人修改fullName时候调用set()
set(value) {
const list = value.split('-');
this.firstName = list[0]
this.secondName = list[1]
}
}
}
})
</script>
</body>
</html>
注意的是 :
- vm._data里面是没有计算属性的
- 计算属性中get函数的this,Vue已经维护好了,并把getter中的this指向调成了vm。
- 书写多次fullName但是getter只用了一次,说明Vue有缓存。如果用上集methods方法,则书写几次方法就调用几次,显然有弊端。
1.3 计算属性简写
确定了只读,不可以修改,才能使用简写方式。
<body>
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{ fullName }}</span> <br/><br/>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: {
// 简写
fullName: function () {
return this.firstName + '-' + this.lastName
}
}
})
</script>
</body>
</html>
2 监视属性
2.1 监视属性
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视!
- 监视的两种写法:
1)new Vue时传入watch配置
2)通过vm.$watch监视
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.1/vue.js"></script>
</head>
<body>
<div id="weather">
<h1>今天天气很 - {{ info }}</h1>
<button @click="changeWeather()">切换天气</button>
</div>
<script type="text/javascript">
new Vue({
el: '#weather',
data: {
judgeHot: true,
},
computed: {
info() {
return this.judgeHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.judgeHot = !this.judgeHot
}
},
watch: {
judgeHot: {
// immediate:true, //初始化时让hander()调用一下, 默认为false
immediate: false,
// handler什么时候调用?当judgeHot发生改变时
handler(newValue, oldValue) {
console.log('judgeHot 被修改了 ...', newValue, oldValue)
}
},
info: {
handler(newValue, oldValue) {
console.log('info 被修改了 ...', newValue, oldValue)
}
}
}
})
</script>
</body>
</html>
2.2 深度监视
- Vue中的watch默认不监测对象内部值得改变(一层)。
- 配置deep:true可以监测对象内部值改变(多层)。
备注:
- Vue自身可以监测对象内部值得改变,但Vue提供得watch默认不可以!
- 使用watch时根据数据得具体结构,决定是否采用深度监视。默认不开启是为了提升效率
<body>
<div id="weather">
<h3>a的值是:{{ numbers.a }}</h3>
<button @click="autoIncrement()">点我让a+1</button>
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#weather',
data: {
numbers: {
a: 1,
b: 2
}
},
computed: {},
methods: {
autoIncrement() {
return this.numbers.a++
}
},
watch: {
// 监视多级结构中某个属性的变化
'numbers.a': {
handler() {
console.log('a has changed ...')
}
}
}
})
</script>
</body>
</html>
<body>
<div id="weather">
<h3>a的值是:{{ numbers.a }}</h3>
<button @click="autoIncrementA()">点我让a+1</button>
<h3>b的值是:{{ numbers.b }}</h3>
<button @click="autoIncrementB()">点我让b+1</button>
<br/><br/>
</div>
<script type="text/javascript">
let vm = new Vue({
el: '#weather',
data: {
numbers: {
a: 1,
b: 2
}
},
computed: {},
methods: {
autoIncrementA() {
return this.numbers.a++
},
autoIncrementB() {
return this.numbers.b++
}
},
watch: {
// 监视多级结构中所有属性的变化
numbers: {
deep: true,
handler() {
console.log('numbers has changed ...')
}
}
}
})
</script>
</body>
</html>
2.3 监视的简写形式
简写的前提是,如果不需要immediate、deep等的配置项,即配置项中只有handler的时候才可以简写。
<body>
<div id="root111">
<h2>今天天气很{{ info }}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root111',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
watch: {
/*isHot:{
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}*/
isHot(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
}
}
})
vm.$watch('isHot', {
handler(newValue, oldValue) {
console.log('isHot 被修改了 ...', newValue, oldValue)
}
})
//vm.$watch简写
vm.$watch('isHot', function (newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
})
</script>
</body>
</html>
3 watch对比computed
两者都能实现的时候,选用computed比较简单,需要异步计算等比较复杂实现的时候用watch。computed和watch之间的区别:
- computed能完成的功能,watch都可以完成。
- watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。
两个重要的小原则: - 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm或组件实例对象。
- 所有不被Vue所管理的函数(定时器的回调函数、Ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm或组件实例对象。
<body>
<div id="watch-name">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el: '#watch-name',
data: {
firstName: 'zhao',
lastName: 'shuai-lc@inspur.com',
fullName: 'zhao#shuai-lc@inspur.com'
},
watch: {
firstName(newValue, oldValue) {
this.fullName = newValue + '#' + this.lastName
},
lastName(newValue, oldValue) {
this.fullName = this.firstName + '#' + newValue
}
}
})
</script>
</body>
</html>
<body>
<div id="watch-name">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{ fullName }}</span>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
let vm = new Vue({
el: '#watch-name',
data: {
firstName: 'zhao',
lastName: 'shuai-lc@inspur.com',
fullName: 'zhao#shuai-lc@inspur.com'
},
watch: {
firstName(val) {
setTimeout(() => {
console.log(this) // vue
this.fullName = val + '-' + this.lastName
}, 1000)
setTimeout(function () {
console.log(this) // window
this.fullName = val + '-' + this.lastName
}, 1000)
},
lastName(val) {
this.fullName = this.firstName + '-' + val
}
}
})
</script>
</body>
</html>