13_Vue中的计算属性
需求
- 两个输入框
- 下方一个全名
- 要求输入框内容发生变化的时候,全名也跟着变化
- 用计算属性很快能写出来,我们先用插值语法和methods配置项完成
methods和插值语法
方法1
使用插值语法和methods配置项完成
思路
- 为input输入框绑定 按键监听事件@keyup OR @keydown
- 发生按键监听的时候,通过类名判断是那个input触发的(event.srcElement.className)
- 然后在通过event的target获取当前元素,然后通过.value获取input输入的内容
- 将值填充到对应的差值区域当中
准备工作
<!-- 创建一个容器 -->
<div class="app">
<!-- 差值语法和methods -->
<div class="box">
<div class="title">差值语法methods</div>
请输入:<input class="full" type="text" @keyup="writeContent"><br>
请输入:<input class="name" type="text" @keyup="writeContent">
<div class="content">你好!{{full}}-{{name}}</div>
</div>
</div>
var vm = new Vue({
el: '.app',
data: {
name: '',
full: ''
},
methods: {
writeContent(e) {
// 判断当前类名
if (e.srcElement.className === "full") {
// 修改差值 full
// e.target获取当前DOM元素, .value获取当前输入框的值
this.full = e.target.value
} else {
// 修改差值 name
// e.target获取当前DOM元素, .value获取当前输入框的值
this.name = e.target.value
}
},
},
});
测试
有点小bug,但是问题不大,功能实现了
出现问题的原因是,重置的时候只是重置了 input当中的value值,并没有重置data当中的变量值
针对方法1的优化(v-model)
- 其实我是通过@keyup这个事件,获取input当中value的值
- 然后在通过事件内部,将value值不断地赋予给 data当中的属性
- 也就是这个事件是一直在被调用的(输入情况下)
- 我们之前是通过v-model从而引出双向绑定的概念,但是我这次居然没有使用它,真是大失误
- 因为v-model默认情况下就是服务于 value属性的,此时不用,更待何时
代码实现
html
<!-- 创建一个容器 -->
<form class="app">
<input type="reset">
<!-- 差值语法 == v-model -->
<div class="box" >
<div class="title">差值语法methods</div>
请输入:<input type="text" v-model="full"><br>
请输入:<input type="text" v-model="name">
<div class="content">你好!{{full}}-{{name}}</div>
</div>
</form>
js
<script>
var vm = new Vue({
el: '.app',
data: {
name: '',
full:""
},
methods: {
},
});
</script>
测试
这个bug依旧存在,因为input当中的reset并不能给vue当中的data清除数据
我们需要重写这个事件,或者通过其他方法来解决
关于methods的实现
怎么个实现方法?
我们首先需要理清下思路
- 我们配置的vue实例对象当中,有许多的配置项,例如当前学习的data和methods
- data和methods当中的属性和函数,最终都挂载到了vue的实例身上
- 当我们使用插值语法的时候,能直接书写属性名or函数名,是因为这些都挂载在vue上面
- 怎么挂载的,后面再说,既然,属性名和函数都在vue身上
- 那么属性能使用插值语法,函数自然也可以使用插值语法
准备工作
<!-- 创建一个容器 -->
<form class="app">
<input type="reset">
<!-- 差值语法 == v-model -->
<div class="box" >
<div class="title">差值语法methods</div>
请输入:<input type="text" v-model="full"><br>
请输入:<input type="text" v-model="name">
<!-- 使用插值语法,将函数插入到其中 -->
<!-- 函数的返回值会作为结果显示在页面当中 -->
<div class="content">{{fullName()}}</div>
</div>
</form>
注意:一定要写完整的函数形式,否则vue会将其作为data当中的属性处理
js
<script>
var vm = new Vue({
el: '.app',
data: {
name: '',
full:""
},
methods: {
fullName(){
return "你好!" + this.full + "-" + this.name
}
},
});
</script>
当我们使用普通函数形式的时候,this的指向就是vue实例,这个没啥多解释的
效果
计算属性
概念
首先我们需要知道一个事情,或者说回顾一个观点
- 在vue的插值语法当中,该插值语法是可以识别表达式和vue当中的属性的
- 所以,关于一些复杂的表达式,我们不应该书写在插值语法表达式当中
- 应该使用更先进的语法 === 计算属性
- 我们来看个例子
来看看这个反例
这是一个插值语法的表达式,但是这个表达式写的很复杂,不易阅读,这是vue的开发理念所杜绝的东西
而计算属性,就是我们接下来需要解读的部分
什么是计算属性
- 在data当中,一个属性分为两个部分组成
- key:属性名
- value:属性值
- 当然,这个就不多比比,上述说的这个东西叫做属性
- 那么什么是 计算属性?
- 很简单,计算机算,就是将你data当中的属性,进行加工,形成一个全新的属性,那么这个就是 计算属性
这个右侧的fullName就是计算属性; = firstName + lastName
全新配置项computed
- 配置方法和data类似,对象的形式
- 内部属性(计算属性),应当配置为 String:Object的形式
- 因为 计算属性,你的表达式可能会很复杂
- get:有人读取fullName的值,get就会被调用,且返回值就是fullName的值
- set:有人修改fullName的属性,会获取到修改的value值,并将这个值重新赋予给fullName
- 这和双向绑定是一个道理
- 先看下写法吧
计算属性与普通属性一样使用插值语法书写
<!-- 创建一个容器 -->
<form class="app">
<input type="reset">
<!-- 差值语法 == v-model -->
<div class="box" >
<div class="title">差值语法methods</div>
请输入:<input type="text" v-model="full"><br>
请输入:<input type="text" v-model="name">
<!-- 使用计算属性 -->
<div class="content">{{fullName}}</div>
</div>
</form>
js
<script>
var vm = new Vue({
el: '.app',
data: {
name: '',
full: "",
},
// 配置计算属性
computed:{
fullName:{
// 有人读取fullName的值,get就会被调用,且返回值就是fullName的值
get(){
return "张三"
},
// 有人修改fullName的属性,会获取到修改的value值,并将这个值重新赋予给fullName
set(){
},
}
}
});
</script>
我们来看下效果
关于get
计算属性/属性
- 计算属性同样是挂载在vue实例上的
- 直接看的话,关于full、name、fullName
- 我们是不能直接看出谁是计算属性谁是普通属性的
细节问题
- 思考一个问题,我们可以通过计算属性对 full和name这两个data当中的属性进行加工
- 那么,我怎么在get当中获取到full和name呢?
- 直接像这样写吗?
- full是没有被定义的,为什么?因为别人在data当中
- data与computed是平级的,fullName这个属性在computed内部,get这个函数又在fullName内部
- 你怎么访问?直接写肯定不行
this的指向
- 用this吗?this的指向是谁?vue吗?为什么
- 因为别人早就提前在get函数当中为我们指定好了this的指向
get什么时候调用
缓存
- 根据之前的说法,有人读取fullName的值,那么get就会被调用
- 那么上述这个图,fullName被调用几次?
- 一次
- 执行到第一次模板当中的fullName后,调用了get
- 随后做了一个缓存,剩下三个地方在读取fullName的时候,就不找get要了,找缓存要
问题
- 既然是找缓存要,那么fullName发生变化的时候,这个缓存不就拉了吗
- 别急,人家还做了一个事情,你可以理解为更新缓存
get调用时机==初次读取
就是上述讲的那个
get调用时机==所在依赖数据发生变化的时候
- fullName依赖的数据是谁? == > full + name嘛
- 以后full或者name的属性值发生了改变,fullName也会随之改变
- 为的就是给你最新的值
- 如果做缓存的时候,不做这件事(所依赖数据发生变化的时候调用)
- 那么就G了
full和name的值发生改动,fullName也会发生改动 == 因为所依赖的数据发生变化
优势
对于这个问题而言,methods是无法做缓存的,而computed是可以做的
关于set
- fullName的值发生修改的时候调用
- 调用的时候set会获取修改的结果(value)
注意:是fullName的值发生变化,不是full和name的值发生变化
我们来看个例子
测试
- 这里显示的不齐全,通过上述测试,fullName的set方法中
- 可以接受到value值
- 但是没有对fullName进行修改,因为我们这里只是做了个无聊的输出
- 那么能不能让fullName = value呢
(精华部分)为什么不能直接修改fullName?
- 可以这样理解,我们在大部分情况下,使用计算属性的时候,常使用的是get
- 对于计算属性而言,他是依赖于其他的属性,会在计算属性的get函数当中,对所依赖的属性进行计算,然后将计算结果返回到插值表达式中
- get的调用时机(2种)
- 第一次加载的时候,做缓存,页面其他地方还有使用了当前计算属性的插值表达式,不需要进行第二次/n次的get加载(很妙的思想)
- 当 计算属性 所依赖的属性 发生变化的时候,get被重新调用
- 以上就是get的调用时机,那么set到底是干嘛的?
- set 是 你 如果需要求改 fullName而非单一修改 full/name的时候,set才会派上用场
- 会接受到你传输的value值,但是这个value值并不能直接赋给fullName,否则就会报错,如上图所示
- 在get的调用时机当中,当 所依赖的属性发生变化 get函数加载
- 那么在set当中,如果你需要修改fullName,那么你必须要把 它所依赖的属性 full 和name一起修改才行
- 我有点感觉,通过get接通了从 full + name 与 fullName这条线
- 而通过set,则接通了从 fullName 到 full + name这条线
那么 为什么 姓和名之间 需要用 -号 分割了知道了吗,因为set的关系,如果不加上 - ,根本无法知道,到底谁属于 full 谁 属于name,所以需要这样
set的使用
// 配置计算属性
computed:{
fullName:{
// 有人读取fullName的值,get就会被调用,且返回值就是fullName的值
get(){
return this.full + "-" + this.name
},
// 有人修改fullName的属性,会获取到修改的value值
set(value){
// full与name的修改会影响到fullName,fullName要修改,那么必须修改full与name
// 对value进行分割
var arr = value.split("-");
this.name = arr[1]
this.full = arr[0]
},
}
}
- 使用split对字符串中的-,进行分割
- 分割完后,将结果赋值给name和full
测试
总结
- 1、
- fullName没有真正的值
- 你每次使用都是vue给算出来的
- 算了还不要紧,vue还会给你把结果缓存下来,发生变动的时候再更新缓存
- 2、
- get与set不能写为箭头函数,否则还是那个老生常谈的问题,this指向会发生改变
- 被vue所管理的函数不要使用箭头函数
- 3、
- 计算属性 如果要被修改,必须要写set函数
- set函数当中,必须要使计算过程中所依赖的属性(full与name)发生改变
总结
计算属性简写方式
要求
确定了该 计算属性,是只读的,不能被修改的(不使用set)
写法
- 使用简写形式,fullName,就不需要写成对象形式了
- 写成function函数形式
- 这个function,就是对应着我们完整写法当中的get
// 简写形式
fullName:function(){
return this.full + "-" + this.name
}
这不就是一个函数的写法吗,那为什么还要这么麻烦呢
// 简写
fullName(){
return this.full + "-" + this.name
}
函数名,就是计算属性的名字,内容则是get函数当中的内容,同样,不能使用箭头函数来代表