vue中的computed和watch

Computed–计算属性

  • 被计算出来的就是计算属性

用户名重复展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 用户名多处展示,使用computed计算属性
new Vue({
data: {
user: {
nickname: "小马",
phone: "18988888888",
email: "593205312@qq.com"
}
},
computed: {
displayName: {
get(){
const user = this.user
return user.nickname || user.phone || user.email
},
set(value){
this.user.nickname = value
}
}
},
template: `
<div>
{{ displayName }}
<div>
{{ displayName }}
<button @click="add">set name</button>
</div>
</div>
`,
methods: {
add({
this.displayName = '小芳'
})
}
}).$mount("#app")

列表展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
let id = 0
const createUser = (name,gender) => {
id += 1
return (id, name, gender)
}

new Vue({
data(){
return{
users: [
createUser("方方", "男")
createUser("小圆", "女")
createUser("小新", "男")
createUser("小葵", "女")
],
gender: '' // 用户选择的 gender 默认为空
}
},
computed: {
displayUsers(){
console.log('displayUsers 计算了一次') // 如果依赖的属性未发生变化,就不会重新计算

const hash = {
male: '男',
female: '女'
}

const {users, gender} = this;
if(gender === ''){
return users
} else if (typeOf gender === 'string') { // 也可写作 (gender === 'male' || gender === 'female')
return users.filter(u => u.gender === hash[gender])
} else {
throw new Error ('gender 的值是意外的值')
}
}
},
methods: {
setGender(string) {
this.gender = string
}
},
template: `
<div>
<div>
<button @click="setGender('')">全部</button>
<button @click="setGender('male')">男</button>
<button @click="setGender('female')">女</button>
</div>
<ul>
<li v-for="(u,index) in displayUsers" :key="index">
{{ u.name }} - {{ u.gender }}
</li>
</ul>
</div>
`
})

缓存

  1. 如果computed依赖的属性没有变化,就不会重新计算
  2. getter/setter 默认不会做缓存,Vue做了特殊处理

Watch监听器

  • 当watch监听的数据发生变化时,执行一个函数

撤销操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
new Vue({
data: {
n: 0,
history: [],
inUndoMode: false // 初始化撤销模式
},
watch: {
n(newValue, oldValue){
console.log(`在不在撤销模式:${this.inUndoMode ? '在':'不在'}`)

if(!this.inUndoMode){
this.history.push({from: oldValue, to: newValue})
}
}
},
template: `
<div>
{{ n }}
<hr/>
<button @click="add1">+1</button>
<button @click="add2">+2</button>
<button @click="minus1">-1</button>
<button @click="minus2">-2</button>
<hr/>
{{ history }}
</div>
`,
methods: {
add1() {
this.n += 1
},
add2(){
this.n += 2
},
minus1() {
this.n -= 1
},
minus2() {
this.n -= 2
},
undo() {
const last = this.history.pop()
console.log(last)

const old = last.from
this.inUndoMode = true
this.n = old // watch是异步的
this.$nextTick(()=> { // 使用 this.$nextTick() 使得该操作在watch的异步操作之后
this.inUndoMode = false
})
}
}
}).$mount("#app")

使用watch模拟computed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
new Vue({
data:{
user: {
email: 'xiaofang@gmail.com',
nickname: '小芳',
phone: '13867676767'
},
displayName: ''
},
watch: {
'user.email'(){
const {user: {email,nickname,phone}} = this
this.displayName = nickname || email || phone
},
'user.nickname'(){
const {user: {email,nickname,phone}} = this
this.displayName = nickname || email || phone
},
'user.phone'(){
const {user: {email,nickname,phone}} = this
this.displayName = nickname || email || phone
}
},
template: `
<div>
{{ displayName }}
<button @click="user.nickname=undefined">remove nickname</button>
</div>
`,
methods: {}
}).$mount("#app")

// 运行代码,displayName并未展示,这是因为watch存在默认选项,也就是第一次设置的值是不监听的,因为第一次赋值是从无到有
// 当点击 remove nickname 时,nickname的值被删除了,watch就监听到了变化

解决watch默认选项问题 immediate: true,第一次的值也要监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
new Vue({
data:{
user: {
email: 'xiaofang@gmail.com',
nickname: '小芳',
phone: '13867676767'
},
displayName: ''
},
watch: { // watch默认选项,第一次的值是不监听的
'user.email': {
handler(){
console.log('email 变了')
const {user: {email,nickname,phone}} = this
this.displayName = nickname || email || phone
},
immediate: true // 第一次渲染也触发 watch
},
'user.nickname': {
handler() {
console.log('nickname 变了')
const {user: {email, nickname, phone}} = this
this.displayName = nickname || email || phone
},
immediate: true
},
'user.phone': {
handler() {
console.log('phone 变了')
const {user: {email, nickname, phone}} = this
this.displayName = nickname || email || phone
},
immediate: true
}
},
template: `
<div>
{{ displayName }}
<button @click="user.nickname=undefined">remove nickname</button>
</div>
`,
methods: {}
}).$mount("#app")

什么是数据变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
new Vue({
data: {
n: 0,
obj: {
a: 'a'
}
},
template: `
<div>
<button @click="n+=1">n+1</button>
<button @click=" obj.a += 'hi' ">obj.a + 'hi'</button>
<button @click="obj = {a: 'a'}">obj = 新对象</button>
</div>
`,
watch: {
n(){
console.log('n 变了')
},
obj(){
console.log('obj 变了')
},
'obj.a'() {
console.log('obj.a 变了')
}
}
}).$mount("#app")
  • obj原本是 {a: ‘a’},后来变为 obj= {a: ‘a’}
  • obj变了吗?变了,因为对象的地址变了;obj.a变了吗?没有
  • 所以简单类型看值,复杂类型(对象)看地址

watch监听更深层的值 deep: true

  • deep: true的意思是,watch监听时,是否往更深层去监听变化
  • 数据变化时,我们一般只比较obj的地址,但在Vue可以做到不仅仅比较地址,还比较里面的数据变化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    watch: {
    n(){
    console.log('n 变了')
    },
    obj:{
    handler() {
    console.log('obj 变了')
    },
    deep: true // 深比较,虽然对象地址没变,但obj里属性的值发生变化,也算obj变了
    }
    }

watch的语法

  • watch的几种写法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // 写法1
    watch: {
    o1: ()=>{}, // 不要使用箭头函数,因为这里的this是全局对象
    o2: function(value, oldValue){},
    o3: (){},
    o4: [f1,f2],
    o5: 'methodName',
    o6: {
    handler: fn,
    deep: true,
    immediate: true,
    'obj.a': function(){}
    },
    }

    // 写法2
    vm.$watch('n', function(){
    console,log('n 变了')
    }), {immediate: true}


    // 写法3:放在钩子里
    created(){
    this.$watch('n', function(){
    console.log('n 变了')
    }, {immediate: true})
    }

computed VS. watch

  1. computed 用来计算出一个值,这个值在调用时不需要加括号,可以当属性用;并且会根据依赖自动缓存,如果依赖不变,computed的值就不会重新计算
  2. watch 用来监听,若某个属性发生变化,就去执行一个函数。它有两个选项:immediate表示第一次渲染时,是否执行这个函数;deep表示监听一个对象时,是否要监听对象里面的变化
0%