深入了解Vue
上一节,我们简单入门了以下Vue,并且体验了一些最基本的功能,本节我们将深入了解Vue
计算属性
属性:Vue认为data里面的东西就可以叫做属性
涉及到属性的处理,我们可以借助于计算属性的方法,也就是借助于computed
注意:计算属性要用已有的属性
computed里面,我们可以写计算得出的属性名以及对应值(对应值必须是对象,且里面必须有get方法)
底层其实就是用到了Object.defineProperty
计算出的属性名不在data上,但仍然再vm上面
get调用的时机:
1.初次读取计算属性时
2.计算属性发生变化时
有get就有set,如果计算属性会被修改,就会调用set
示例代码:
<body>
<div id="root">
姓:<input type="text" v-model="firstName"><br/>
名:<input type="text" v-model="lastName"><br/>
姓名:<span>{{fullName}}</span>
</div>
<script>
new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三'
},
computed:{
fullName:{
get(){
return this.firstName+'-'+this.lastName
},
set(val){
const arr=val.split('-')
this.firstName=arr[0]
this.lastName=arr[1]
}
}
}
})
</script>
</body>
简写:
如果我们需要的计算属性中,不涉及修改,那么可以按如下简写,这里面的function就当作get用:
computed:{
fullName:function(){
return this.firstName+'-'+this.lastName
}
}
监视属性
监视属性需要借助watch,里面写你要监视的对象名,该对象的值应该是一个包含handler函数的对象
handler函数在监视的属性变换时调用,其可以接受两个参数,分别是新值和旧值
代码实现:
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="change">切换天气</button>
</div>
<script>
new Vue({
el:'#root',
data:{
isHot:true
},
methods:{
change(){
this.isHot=!this.isHot
}
},
computed:{
info:function(){
return this.isHot?'炎热':'凉爽'
}
},
watch:{
isHot:{
handler(newVal,oldVal){
console.log('isHot被修改了',newVal,oldVal)
}
}
}
})
</script>
</body>
当然监视属性的值除了handler以外,还有别的配置
除了data里的属性可以被监视,计算属性也可以被监视
我们还可以不用watch实现监视属性:
vm.$watch('isHot',{
handler(){
console.log('isHot被修改了',newVal,oldVal)
}
})
深度监测
我们看一个案例:
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="change">切换天气</button>
<hr/>
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">a加一</button>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1
}
},
methods:{
change(){
this.isHot=!this.isHot
}
},
computed:{
info:function(){
return this.isHot?'炎热':'凉爽'
}
},
watch:{
isHot:{
handler(newVal,oldVal){
console.log('isHot被修改了',newVal,oldVal)
}
}
}
})
</script>
</body>
我现在希望监视a,要用如下方式:
'numbers.a':{
handler(newVal,oldVal){
console.log('a变化了',newVal,oldVal)
}
}
如果我希望监视整个numbers里面的所有内容,直接监视numbers是不行的,因为对象表面不变
所以我们希望“深度”监视numbers,我们要借助deep属性
numbers:{
deep:true,
handler(){
console.log('numbers变化了')
}
}
监视的简写形式(只有handler时才可以)
isHot(newVal,oldVal){
console.log('isHot被修改了',newVal,oldVal)
}
如果是另一种监视方式,可以这样简写(不要写箭头函数):
vm.$watch('isHot',function(){
console.log('isHot被修改了',newVal,oldVal)
})
绑定样式
class样式绑定
对于要改变的样式,我们要按如下形式写:
<div :class="abc">AAA</div>
接下来我们可以把abc加入属性中去:
data:{
abc:'basic'
}
注意这里的basic是一个已有样式,abc可以看作是变量
上述方法适用于样式类名不确定,需要动态指定的绑定class选择器的情况
上面的abc值除了可以是一个字符串,还可以是数组,用于实现多个样式叠加
如果样式的名字和个数都确定,只是用不用不确定,可以用对象写法:
data:{
abc:{
basic1:true,
basic2:false,
}
}
style样式绑定
我们可以这样写:
<div :style="{fontSize: fsize+'px';}">AAA</div>
这样我们就可以把fsize写在data属性里面去操作它了
当然,我们也可以直接维护一个对象:
<div :style="styleObJ">AAA</div>
此时data里就可以这样:
data:{
styleObj:{
fontSize:'40px'
}
}
当然绑定style样式也支持数组写法,但是不常用
条件渲染
v-show="布尔值表达式或布尔值变量"可以控制某一元素隐藏或者显示(底层是改变display属性)
v-if也可以做条件渲染,这里的不显示会连结构都去除,后面也是加布尔值变量或表达式
如果某个节点变化频率很高,推荐用v-show
注意:也有v-else-if和v-else这种构成选择语句(要与v-if搭配,不能被打断)
对于一些条件一样的判断,我们可以用template标签包起来(注意只能与v-if配合用)
列表渲染
我们可以借助v-for实现一种列表元素的遍历渲染
案例:
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<li v-for="p in persons" :key="p.id">{{p.name}}-{{p.age}}</li>
</ul>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'John',age:18},
{id:'002',name:'Jeson',age:19},
{id:'003',name:'Perl',age:20},
]
}
})
</script>
</body>
注意:一定要配置key这个特殊属性,值应该是id
不用id还可以借助数组的索引值(一般不用):
<li v-for="(p,idx) in persons" :key="idx">{{p.name}}-{{p.age}}</li>
v-for除了遍历数组,还可以遍历对象:
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<li v-for="(v,k) in car" :key="k">{{k}}-{{v}}</li>
</ul>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
car:{
name:'奥迪A8',
price:'200万',
color:'白色'
}
}
})
</script>
</body>
当然v-for还可以遍历字符串
key作用与原理
key就是一个标识的作用
key不能用index的原因:
因为虚拟DOM的diff算法,首先对比的就是key,如果key相等,就导致,里面的元素只要一样就会复用,不一致才新生成节点
这里面就会出现匹配不一致的问题,如果key不等,里面元素就不比较了,直接重新生成节点
所以key一定要用唯一标识符
列表过滤
所谓列表过滤就是从列表元素中,过滤出符合条件的元素
我们先借助监视属性实现这个功能:
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" v-model="keyWord">
<ul>
<li v-for="p in filpersons" :key="p.id">{{p.name}}-{{p.age}}</li>
</ul>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'John',age:18},
{id:'002',name:'Jeson',age:19},
{id:'003',name:'Perl',age:20},
],
filpersons:[]
},
watch:{
keyWord:{
immediate:true,
handler(newVal){
this.filpersons=this.persons.filter((p)=>{
return p.name.indexOf(newVal)!==-1
})
}
}
}
})
</script>
</body>
我们再借助计算属性实现:
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" v-model="keyWord">
<ul>
<li v-for="p in filpersons" :key="p.id">{{p.name}}-{{p.age}}</li>
</ul>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'John',age:18},
{id:'002',name:'Jeson',age:19},
{id:'003',name:'Perl',age:20},
]
},
computed:{
filpersons(){
return this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord)!==-1
})
}
}
})
</script>
</body>
因为计算属性在一开始就会执行一次,所以用computed更好
列表排序
假设我们希望按年龄进行排序
代码可以这样写:
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" v-model="keyWord">
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=0">原顺序</button>
<ul>
<li v-for="p in filpersons" :key="p.id">{{p.name}}-{{p.age}}</li>
</ul>
</div>
<script>
const vm=new Vue({
el:'#root',
data:{
keyWord:'',
sortType:0,//0表示原顺序 1表示降序 2表示升序
persons:[
{id:'001',name:'John',age:19},
{id:'002',name:'Jeson',age:17},
{id:'003',name:'Perl',age:20},
]
},
computed:{
filpersons(){
const arr=this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord)!==-1
})
if(this.sortType){
//这里箭头函数放在了普通函数内部,箭头函数的this就是普通函数的this
//事实上箭头函数的this取决于函数作用域
arr.sort((a,b)=>{
return this.sortType===1?b.age-a.age:a.age-b.age
})
}
return arr
}
}
})
</script>
</body>
监视数据原理
监视对象数据改变的原理
事实上,我们写完data的内容时,vm在将其存入_data中的时候,将属性变成了get,set的形式
其具体过程我们简单地可以理解为下面这样:
1.创建一个观察者类型,并且创建一个观察者实例
2.定义观察者类型的构造函数,构造函数会遍历data中的属性,并且通过Object.defineProperty的方式赋予观察者实例
3.让vm._data与data都等于观察者实例
如此一来,就实现了对数据的监视,数据只要发生改变,set方法就会被调用,这样看似麻烦,事实上解决了如果直接把属性定义在data上出现反复调用导致爆栈的问题,又保证了_data与data的一致性
如果想要后添加的属性也有响应式(有相应的get,set),要用Vue.set这个API,用法见下面的例子:
Vue.set(vm.student,'sex','男')
但是注意:这个API不能直接给data增加属性
监视数组数据改变的原理
如果响应式对象是一个数组,我们直接改了数组里面某个索引的值,Vue并不会监测到
Vue对数组的监视,是看是不是调用了push,pop,shift,unshift,splice,sort,reverse方法
我们调用Vue管理的数据的push等函数,实际上是Vue自己定义的push,它多了一步重新解析模板的步骤
除了这样,我们借助Vue.set API也可以实现对数组元素改变影响视图更新
Vue.set(vm.student.hobby,index,'xxx')