复杂data的处理方式
我们在模板中可以直接通过插值语法显示一些data中的数据。
但是在某些情况下,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
- 比如我们需要对多个data数据进行运算、三元运算符来决定结果、数据进行某种转化后显示
- 在模板中使用表达式,可以非常方便的实现,但是设计它们的初衷是用于简单的运算
- 在模板中放入太多的逻辑会让模板过重和难以维护
- 并且如果多个地方都使用到,那么会有大量重复的代码
我们怎么把逻辑抽离出去呢?
- 使用methods,把逻辑抽取到methods的方法中
- 但是这有一个直观的弊端,就是所有data使用过程都变成了一个方法的调用
-
认识计算属性computed
什么是计算属性?
官方并没有给出直接的解释
- 而是说:对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性
- 计算属性将被混入到组件实例中。所有getter和setter的this上下文自动地绑定为组件实例
计算属性的用法:
- 选项:computed
类型:{ [key: string]: Function | {get: Function,set: Function} }
案例:
案例一:我们有两个变量,firstName和lastName,希望它们拼接之后在界面上显示
案例二:我们有一个分数:score当score大于60的时候,在界面上显示及格
- 当score小于60的时候,在界面上显示不及格
案例三:我们有一个变量message,记录一段文字:Hello World
- 某些情况下我们直接显示这段文字
某些情况下我们对这段文字进行反转
<template>
<h2>{{fullName}}</h2>
<h2>{{result}}</h2>
<h2>{{reverseMessage}}</h2>
</template>
<script>
data() {
return {
firstName: 'Kobe',
lastName: 'Bryant',
score: 80,
message: 'Hello World'
}
},
computed: {
fullName() {
return this.firstName + this.lastName
},
result() {
return this.score >= 60 ? "及格" : "不及格"
},
reverseMessage() {
return this.message.split(" ").reverse()
}
}
</script>
计算属性 vs methods
计算属性和methods的实现看起来差别不大,但是计算属性是有缓存的
接下来我们看一下同一个计算多次使用,计算属性和methods的差异:
计算属性是有缓存的,当我们多次使用计算属性时,计算属性中的运算只会执行一次
计算属性会随着依赖数据的改变,而进行重新计算计算属性的缓存
为什么计算属性只做了一次打印呢?
这是因为计算属性会基于它们的依赖关系进行缓存
- 在数据不发生变化时,计算属性是不需要重新计算的
- 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算
计算属性的setter和getter
计算属性在大多数情况下,只需要一个getter方法即可,所以我们会将计算属性直接写成一个函数
计算属性的完整写法:
computed: {
// fullName的getter写法
fullName: function(){
return this.firstName + this.lastName
}
// fullName的setter和getter写法
fullName: {
get: function() {
return this.firstName + this.lastName
},
set: function(newValue) {
console.log(newValue)
}
}
},
methods:{
changeFullName() {
this.fullName = "kobe"
}
}
这样写,当我们执行changeFullName方法,内部的this.fullName = “Kobe”就会将Kobe传给fullName的set方法,Kobe就是newValue,最终会打印“kobe”,我们实际业务中如果需要对计算属性做修改,那么在set函数中定义如何修改即可。
vue中computed源码阅读
vue中,通过解构语法获取整个实例的options,computed也在其中,并且vue给computed赋了一个别名computedOptions。
第一步:遍历computedOptions,遍历时会拿到computed中的key,再通过computedOptions[key]的方式获取值,这个值既可能是一个函数,也可能是一个对象,所以做了一个是否为函数的判断,如果是函数那么就给这个函数绑定publicThis(一个vue定义的对象,包含一些vue内部实现的功能以及当前实例的上下文等),再用一个常量get接收函数执行后的返回值;如果不是函数,那它就是一个对象,vue就获取这个对象的get值,把前面绑定publicThis的步骤在获取到的这个get上做一遍;如果都没有,就返回一个空函数,说明没有get。并且如果获取到了get,那么就说明它有set,将set也做一遍前面的是否为函数的判断,如果是函数也绑定publicThis,不是的话返回空函数。
第二步:第一步的目的就是获取get以及set并且给他们绑定上下文,然后通过vue内部实现的computed函数给get和set添加上响应式的效果(具体效果等学习到响应式再具体学习),最终实现computed的功能。
认识侦听器watch
什么是侦听器?
- 开发中我们在data返回的对象中定义了数据,这个数据通过插值语法等方式绑定到template中
- 当数据变化时,template会自动进行更新来显示最新的数据
- 但是在某些情况下,我们希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器watch来完成了
侦听器的用法如下:
- 选项:watch
类型:{ [key: string]: string | Function | Object | Array }
侦听器的配置选项
深度侦听
立即执行
侦听器的其他用法
以字符串的形式使用方法
data() {
return {
b: 'b'
}
},
watch: {
b: 'someMethod'
},
methods: {
someMethod(newVal,oldVal){
}
}
回调数组
f: [
"handle1":,
funcion handle2(newVal, oldVal) {
console.log("handle2 triggered")
},
{
handler: function handle3(newVal, oldVal) {
console.log("handle3 triggered")
}
}
]
侦听对象的某个属性
"info.name": function(newVal, oldVal) {
console.log(newVal, oldVal)
}
$watchAPI
我们可以在vue的生命周期函数中使用this.$watchs来侦听:
第一个参数是要真听的源数据
- 第二个参数是侦听的回调函数callback
- 第三个参数是额外的其他选项,比如deep、immediate
created() {
this.$watch('info', (newVal, oldVal) => {
console.log(newVal)
},{ deep: true, immediate: true })
},