计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护
- 模板内表达式用于简单计算
{{text.split('').reverse().join('')}}
- 计算属性适用于复杂计算
<div id='app'>
{{reverseText}}
</div>
<script>
var app = new Vue({
el: '#demo',
data: {
text: '123,456,789'
},
computed: {
reverseText:function(){
return this.text.split(',').reverse().join(',');
}
}
})
</script>
计算属性方法
- 计算属性可以依赖其他计算属性
- 计算属性可以依赖其他实例的数据
- 计算属性的属性值必须是一个函数
- 计算属性不能执行异步任务,异步任务交给侦听属性
- 在 getter 中收集依赖,在 setter 中触发依赖
<div id="app">
{{fullName}}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
firstName: 'li',
lastName: 'bai'
},
computed: {
fullName:{
get: function(){
return this.firstName + ' ' + this.lastName
},
set: function(newValue){
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[1]
}
}
}
})
现在运行app.fullName = 'zhang hua',setter会被调用,app.firstName和app.lastName也会随之改变
</script>
getter
如果计算属性直接接一个函数,默认属性时 getter
computed: function(){
}
setter
手动修改计算属性值会触发 setter
set 设置属性,并不是直接修改计算属性,而是修改它的依赖。
计算属性缓存
计算属性是基于它们的响应式依赖进行缓存的,一个计算属性所依赖的数据未发生变化,计算属性就不更新
methods 方法具有相同效果,还可以接受参数,使用更灵活。
watch、computed、methods 比较
- computed 是依赖与其他属性的一个计算值,依赖于缓存,只有当其依赖的数据发生变化时它才更新
- watch 是监听的属性发生变化时,触发回调执行逻辑
- computed 是计算出一个属性,watch 是上报数据等
- computed 与 methods 的选择取决于你是否需要缓存,computed 属性的依赖不发生变化就不会重新计算,遍历大数组和做大量计算时使用计算属性。methods 是看到一次计算一次
侦听属性
观察和响应 Vue 实例上的数据变动,watch
参考:Vue.js 的 computed 和 watch 是如何工作的?- 慕晨同学
<div id="app">
<div>fullName: {{fullName}}</div>
firstName: <input type="text" v-model="firstName">
</div>
<script>
var app = new Vue({
el: '#app',
data: {
firstName: 'li',
lastName: 'bai',
fullName: 'li bai'
},
watch: {
firstName(newName, oldName) {
console.log('第一次没有执行')
this.fullName = newName + ' ' + this.lastName;
}
}
})
</script>
通过下图可以看出,初始化时 watch 是不执行的,当 firstName 发生改变时,watch 才监听计算
handler 用法
绑定 handler 方法,immediate:true; 立即执行 handler 方法
var app =new Vue({
el: '#app',
data: {
firstName: 'li',
lastName: 'bai',
fullName:''
},
watch: {
firstName: {
handler(newName,oldName){
console.log('第一次执行了')
this.fullName = newName + ' '+ this.lastName
},
immediate:true
}
}
})
immediate:true 立即执行。immediate:false 时与例子 1 相同,初始化时控制台不会出现信息
deep 用法
deep:true 深度监听
var app = new Vue({
el: '#app',
data: {
obj: {
a: 1,
b: 2,
c: 3
}
},
watch: {
obj: {
handler(val) {
console.log('obj.a changed')
},
immediate: true,
// deep: true
}
}
在 input 框输入数据不是响应式的(数据不在 data 对象上,在更深层的位置),hanlder 方法在输入数据时不执行
deep:true 深度监听 obj 里的属性值,会在对象层层往下遍历,使每层都加上监听器,触发深层对象的依赖,追踪其变化,使 handler 方法每次能够执行。
缺点
每层加上监听器,性能开销太大
- 优化方法:
- 用字符串方式,给'obj.a'属性设置监听函数
var app = new Vue({
el: '#app',
data: {
obj: {
a: 1,
b: 2,
c: 3
}
},
watch: {
'obj.a': {
handler(val) {
console.log('obj.a changed')
},
immediate: true
}
}
})
class 与 style 绑定
用 v-bind 处理
绑定 class 的几种方式
对象语法
传给 v-bind:class 一个对象,以动态地切换 class。
<div id="app">
<div :class="{active:isActive}"></div>
</div>
isActive值为true/false
var app = new Vue({
el: '#app',
data: {
isActive: true
}
})
数组语法
把一个数组传给 v-bind:class,以应用一个 class 列表。
<div :class="[activeClass,errorClass]"></div>
var app = new Vue({
el: '#app',
data: {
activeClass: 'active',
errorClass: 'error'
}
})
数组对象混合语法
<div id="app">
<div v-bind:class="[{'active':isActive},errorClass]"></div>
</div>
.active {
width: 100px;
height: 100px;
background: lightgreen;
}
.error {
border: 5px solid;
}
var app = new Vue({
el: '#app',
data: {
isActive: true,
errorClass: 'error'
}
})
用在组件时
在一个自定义组件上使用 class property 时,class 被添加的组件的更元素上但是不会覆盖原有的 class,共存。
<div id="app">
<my-component :class="{active:isActive}"></my-component>
</div>
.active {
width: 100px;
height: 100px;
background: pink;
}
Vue.component('my-component',{
template: '<div class="foo">hi</div>'
})
var app =new Vue({
el: '#app',
data: {
isActive: true
}
})
绑定内联样式
对象语法
<div id="app">
<div v-bind:style="{color: activeColor, fontSize: fontSize + 'px'}">hello world</div>
<div v-bind:style="styleObject">hello vue</div>
</div>
var app = new Vue({
el: '#app',
data: {
activeColor: 'red',
fontSize: 20,
styleObject: {
color: 'green',
font: 16
}
}
})
数组语法
<div id="app">
<div v-bind:style='[styleA,styleB]'>hello</div>
</div>
var app = new Vue({
el: '#app',
data: {
styleA: {
color:'red',
background: 'yellow'
},
styleB: {
border: '3px solid black'
}
}
})
内置指令
基本指令
v-cloak
解决初始化慢导致页面闪动。
<div id="app">
<div v-cloak>{{msg}}</div>
</div>
[v-cloak] {
display: none;
}
var app = new Vue({
el: '#app',
data: {
msg: 'hello'
}
})
//输入msg可以观察到变化
v-once
只渲染元素和组件一次
条件渲染指令
v-if、v-else、v-else-if
<div id="app">
<div v-if="6<3">{{correct}}</div>
<div v-else-if='9>5'>{{correct}}</div>
<div v-else>{{error}}</div>
</div>
var app = new Vue({
el: '#app',
data: {
correct: 'true',
error: 'false'
}
})
用 key 管理可复用元素
问题: 元素复用
弊端:处于效率考虑,会尽可能复用已有的元素(切换用户/密码框,input 填写内容不变)
解决方法: 用 key 管理可复用的元素,表达他们是独立的两个元素,不要复用
<template>元素上使用 v-if 条件渲染分组
若是想切换多个元素,可以在<template>元素上使用 v-if 包裹,最终渲染结果不包括<template>元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-show
根据条件展示语句,只改变 css 属性 display,带有 v-show 的元素始终会被渲染并保留在 DOM 中 实例
v-if 与 v-show 比较
- v-if 实时渲染,显示才开始渲染,不显示就移除
- v-show 元素永远存在页面中,总是渲染,只改变 display 属性
- 选择:频繁切换使用 v-show,条件很少改变使用 v-if
列表渲染指令
v-for
将一个数组遍历或枚举一个对象属性的时候循环显示
语法
item in items // item是源数组数据,items是被迭代的数组元素的别名
(item,index) in items
value in object
(value,name,index) in object
遍历多个对象
<div id="app">
<ul>
<li v-for="item in items" :key="item.message">
{{item.message}}
</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
items: [
{message: 'hello'},
{message: 'world'}
]
}
})
</script>
注意:v-for默认采用“就地更新” 在使用 v-for 时提供 key attribute,在数据项改变时可以跟踪每个节点,重新排序,而不是就地更新
<div id="app">
<ul>
<li v-for="(item,index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
parentMessage: 'Parent',
items: [
{message: 'hello'},
{message: 'world'}
]
}
})
</script>
遍历一个对象的多个属性
<div id="app">
<ul>
<li v-for="value in Object">
{{value}}
</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
Object: {
Name : 'Vue',
Date: '2020'
}
}
})
</script>
v-if 和 v-for 能否一起使用
- 不推荐 v-if 和 v-for 一起使用(v-for 比 v-if 级别更高)
- v-if 和 v-for 一起使用出现的优先级问题:实例
数组更新
变更方法
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
替换数组
不变更原始数组,总是返回一个新数组
- filter()
- concat()
- slice()
computed: {
matchO: function(){
return this.items.filter(function(items){
return items.match(/o/);
})
},
concat: function(){
return this.items.concat('four');
},
slice: function(){
return this.items.slice(2)
}
}