肝vue的第三天,今天说是第三天其实今天没怎么看代码,也没有看视频和文档,周六嘛,稍微锻炼身体去了,但是昨天写的东西蛮多了,发了一次没发完,今天补发一下。
计算属性computed
我们知道,如果我们需要将vue实例中的两个属性拼接起来作为一个新的属性或者值来使用的话,我们可以使用插值语法{{属性1}}{{属性2}}进行拼接,当然也可以在内部写表达式拼接,但是vue的风格指南指出我们应该将简单的表达式写在插值语法里,复杂的表达式应该写在函数或者其他的语法表达出来。
举个例子,现在vue实例中有两个属性一个fristName一个lastName,我们需要将姓与名进行拼接显示(可能还会含有特殊的处理):
姓:
名:
全名:{{fristName.slice(0,3)}}-{{lastName.slice(0,3)}}
const vm = new Vue({
el: '.app',
data() {
return {
fristName: '花',
lastName: '木兰'
}
}
})
现在我们采用插值语法直接在标签内容中写出插值表达式,这种编码是vue指南所不推荐的!
那么我们换个方式,用vue实例中methods中的方法实现拼接呢?将拼接的返回值利用插值语法给到标签内容不就行了?
全名:{{fullName()}}
methods: {
fullName() {
return this.fristName + '-' + this.lastName
}
},
但是这种方式大家都清楚,我们每一次访问这个fullName()它都会触发函数开辟线程去运算,对于系统性能有损耗。能不能有一种可以不改变初始计算之后就会同步到缓存上去的特殊方式呢?(这样的话只要不是更改拼接值,就不会开启第二次运算了)vue提供了一种特殊的语法,就是所谓的“计算属性”,计算属性是一种利用数据代理思想所新增的伪属性,它底层也是我们之前说过的Object.defineProperty();方法实现,那么它是怎么写的呢?
全名:{{fullName1}}
// vue中对已有的属性进行计算的特殊语法就是计算属性 相当于数据代理新增属性fullName 是新属性调用就是直接写名字!!!
computed: {
fullName1: {
// 这里配置的getter方法就是和Object.defineproperty()方法里属性的配置项里的getter方法作用一致
// get调用的时机:
// 1.初次读取fullName时(初次读取之后会建立缓存,之后访问就不会调get方法)
// 2.所依赖的属性(参与计算的data中的属性)发生变化时
get() {
console.log('get被调用了')
// 这里的this依旧是vue实例
return this.fristName + '-' + this.lastName
},
// 在更改计算属性fullName时调用,如果不在内部更新data中的属性则就没啥意义
set(value) {
console.log('fullName1计算属性被修改为' + value)
// 实现异步更新data里的属性进行缓存刷新(调用get())
const arr = value.split('-')
this.fristName = arr[0]
this.lastName = arr[1]
}
}
}
这里配置的getter方法就是和Object.defineproperty()方法里属性的配置项里的getter方法作用一致
get()调用的时机:
1.初次读取fullName时(初次读取之后会建立缓存,之后访问就不会调get方法,直接去缓存读取值) ;
2.所依赖的属性(参与计算的data中的属性)发生变化时。
而同样的计算属性依赖于data中的两个属性存在,如果单纯的更改计算属性的值而不在它的set()方法里异步更新data中所依赖属性的值的话就会出现无效更改的情况。
从上图中我们不难发现所谓的计算属性也是和属性一样可以由vue实例直接去访问的。
那么计算属性的简写是?其实我们不难看出计算属性就是为了对vue实例中data的属性进行组合或者运算所准备的,那么我们主要就是关注它的get()方法,至于它的set()方法则是次要关注的,所以当我们只需要配置计算属性的get()方法时,我们可以像写methods中的方法那样书写计算属性:
computed:{
// 当我们的计算属性不需要配置set方法时,我们可以将其写成函数,这个函数就是getter
fullName2: function () {
console.log('简写计算属性')
return null
}
}
监视属性watch
何谓监视属性(监视属性与计算属性不一样,监视属性只记录所监视data中的属性的变化,有变化则有动作,无变化默认无执行动作,它不是一个插值语法可以直接取值的属性)?我们先来看个需求:再之前的js中我们能不能实现记录一个数据变化的大小值?
例如一个班级本次考试较于上次考试,数学平均成绩的变化值?可能我们可以用两个成绩变量来存储,一个newMark,一个oldMark来记录上次成绩与本次成绩(当然数组也可以而且可以存储更多,这里只用于聊监视属性的用途之一),那么我们就需要对变量进行更新的记录,以及差值的计算然后渲染到页面上去。但是vue为我们提供了一个很nice的属性,就是监视属性,它可以记录一个属性的两次变化并且内部含有一个handler(oldValue,newValue){}函数,可以对改变前后的值进行处理;
举个例子,我们现在有一个容器记录一个mark标记的值,vue实例挂载在它上面,我们在vue中设置属性mark以及可以改变mark的函数:
你说慌了吗?{{mark}}
那么我们想知道这个mark前后的变化,所以我们在vue实例中设置一个监视属性(跟在methods后面即可):
// 监视,专门为了监视vue已有属性变化(计算属性也属于vue新增的一种属性,可以对计算属性进行监视)
watch: {
mark: {
//这个参数为true就是在脚本加载的时候就执行一次handler方法,所以那时候的oldValue应该是个undefined
immediate: true,
// handler函数在所监视属性修改时调用 (因为immediate默认是false 不立刻执行)
handler(newVal, oldVal) {
console.log(oldVal, newVal)
}
},
}
控制台初次渲染打印:
下面是生产信息提示,可以忽略掉。当我把immediate属性设置为false或者哪行代码删掉时就没有这行打印结果。
我们也知道vue中的对象或者属性都是键值对模式的,也就是key : value 格式的,而几乎所有的key都是字符串格式的,那么其实mark的真实写法应该是 ‘mark’ ,同理我们如果需要监视一个双层级属性呢?例如(监视marks对象中的mark1属性?):
marks: {
mark1: 100,
mark2: 99
}
如果我们直接写
marks.mark1:{
handler(oldV,newV){}
}
绝对会报错!!!应该是(注意key的写法,单层直接写不加 ‘’ 那是可以的,一旦多层就需要加字符串符号了):
// 多层级监视marks对象中的mark1:
'marks.mark1': {
handler() {
console.log('mark1被改变了')
}
},
如果我需要监视整个对象里全部属性的变化呢?(注意我这里说的是全部属性的变化,而不是任一属性的变化,因为监视分为普通模式和深度模式):
我们只需要和监视属性一样写监视对象的代码就可以了,但是这里的handler函数只会在对象中的全部属性同时改变时触发!!!因为仅更改部分是不会触发地址引用的变化的,也就默认没有更改(对象无法对比值,只能对比地址引用值)。这里我们就需要开启深度监视模式了:
// 多层级监视marks中全部属性的变化(任一属性变化了都会触发handler函数执行需要开启深度监视)
marks: {
deep: true,//开启深度监视,默认情况为false不监视对象内部属性的改变(但是vue实例是可以监视到的只是你的watch默认不行)
handler() {
console.log('marks对象发生了变化')
}
}
这里提一下监视的简写,当我们除了handler函数之外没有其他的配置项的时候,我们可以以下面的格式书写监视(当然需要在watch:{}里):
// 监视属性的简写方法就是当监视配置项里只有handler函数时可以将属性名直接写成函数
mark(oldVal,newVal){console.log('mark发生改变',oldVal,newVal)}
那么之前我们用vm接收vue实例的时候,挂载我们可以利用$mount('选择器名')进行,这里的监视我们也可以用vm.$watch('监视的属性名',{配置项和vue实例中的写法一致})例如:
// 第二种配置监视的方式:直接用VM对象接收vue实例调用它的$watch()方法实现配置
VM.$watch('mark', {
immediate: true,
handler(newVal, oldVal) {
console.log(oldVal, newVal)
}
})
// 它的简写方式就是当监视除了handler函数之外没有其他配置项时可以写成:
VM.$watch('mark',function(oldVal, newVal){
console.log('mark发生改变',oldVal, newVal)
})
绑定样式
v-bind:class="属性名(可以是数组也可以是字符串抑或是对象)"
之前在原生js中我们是通过document.getElementById('id').className("class名")进行样式的绑定 与更改。现在在vue的v-bind绑定命令中,我们不仅可以绑定数据,绑定href等带有描述性值的标签属性,我们也可以绑定标签的样式:
举个例子:我们准备四个样式,一个基本样式,三个变化样式,一个容器和一个挂载在它上面的vue实例:
vue实例:
const vm = new Vue({
el: '#app',
data() {
return {
// 数组写法
moods: ['normal', 'happy', 'sad'],
mood: 'normal',
index: 0,
// 对象写法
classObj: {
happy: true
}
}
},
methods: {
getMood() {
// 规则更改按顺序
if (this.index > 1) this.index = -1
this.mood = this.moods[++this.index]
// 不规则更改即是随机生成012三个数字其中的一个,将其作为索引取值赋给mood即可
// Math.floor(Math.random()*3);
}
},
})
我们不难发现,上面的class的值给到了data中的mood而mood的值存在于数组之中,我们就可以通过让mood对数组遍历值的接收来动态的通过methods方法来改变绑定的样式了。
但是不仅仅是class呀还有内部样式style的设置呢?
绑定style
下面是vue实例
new Vue({
el: '#cdd',
data() {
return {
// 对象式修改style
styleObj: {
// vue对style组合词支持小驼峰
fontSize: '40px'
}
}
},
methods: {
getFontSize() {
this.styleObj.fontSize = '10px'
}
}
})
只能一次性改变标签内部字体的大小,但是这就是改变style的方法,当然至于多种类型的这里就不一 一举例了。
自定义vue实例代码段
这里还是和我们之前配置html代码模板的方式一样,我们直接去配置代码段选区里自定义vue的代码段:
"Print to new Vue": {
"scope": "javascript,typescript",
"prefix": "vue",
"body": [
"new Vue({",
"\tel:'',",
"\tdata(){",
"\t\treturn{",
"\t\t}",
"\t}",
"})"
],
"description": "生成Vue实例代码模板"
}
只要输入vue就会出现整个代码段的提示。