数据改变(vue视图上的数据能渲染成功)时会触发对应的监听器watch。
vue提供了watch方法,用于监听实例内data数据的变化。

一、普通类型字符串等监听

直接监听

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
<template>
<div class="about">
<!-- v-bind只视图绑定数据,视图修改无法触发监听watch方法,只能数据修改触发watch并渲染视图 -->
<!-- v-model实现双向数据绑定,视图修改触发监听watch方法 -->
<div>
<input type="text" :value="name" />
<button @click="change1">更新视图</button>
</div>
<div>
<input type="text" v-model="age" />
<button @click="change2">更新视图</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
name: "lilu",
age: 20,
};
},
methods: {
change1() {
this.name = this.name + "1";
},
change2() {
this.age = this.age + "1";
},
},
watch: {
// 修改字符串、数组直接渲染视图,监听到改变
name(newVal, oldVal) {
console.log("name:newVal--" + newVal + ",oldVal--" + oldVal);
},
age: function (newVal, oldVal) {
console.log("age:newVal--" + newVal + ",oldVal--" + oldVal);
},
}
}
</script>

二、对于对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div class="about">
<input type="text" :value="JSON.stringify(friend)" style="width: 300px" />
<input type="text" :value="friend.friendName" />
<button @click="changeFriendName">更新视图</button>
</div>
</template>
<script>
export default {
data() {
return {
obj: "oo",
friend: {
friendName: "yin",
age: 24,
},
};
},
}
</script>

监听对象本身

直接监听对象是监听对象的指向,不能监听到某个对象里面属性的变化。
如果需要监听的数据是对象内的某一属性值的变化,直接watch对象friend是检测不到变化的,这是因为friend这个对象的指向并没有发生改变。

1
2
3
4
5
6
7
8
9
<script>
export default {
watch: {
friend(newVal, oldVal) {
console.log("friend:newVal--" + newVal + ",oldVal--" + oldVal);
}
}
}
</script>

触发监听

直接改变对象会触发监听

1
2
3
4
this.friend = {
friendName: "yin",
name: "lu",
};

监听对象内属性变化

方法一:深度检测

deep设为了true,修改了这个friend中的任何一个属性,都会执行handler这个方法。不过这样会造成更多的性能开销,尤其是对象里面属性过多,结构嵌套过深的时候。而且有时候我们就只想关心这个对象中的某个特定属性。

1
2
3
4
5
6
7
8
9
10
11
12
<script>
export default {
watch: {
friend: {
handler(newVal, oldVal) {
console.log("deep friend:newVal--" + newVal + ",oldVal--" + oldVal);
},
deep: true,
},
}
}
</script>

扩展:handler声明函数或函数表达式都可以,但是不能箭头函数

1
2
3
4
5
6
7
8
9
watch : {
newA : {
handler : function (newVal, oldVal) {
console.log(newVal,oldVal)
},
immediate : true, //初始化页面后立即监听
deep: true,
}
}

immediate属性

没有的时候上面的例子是值变化时候,watch才执行,我们想让值最初时候watch就执行就用到了handlerimmediate属性,immediate : true代表在wacth里声明了newA这个方法之后立即先去执行handler方法,如果设置了false,那么效果和没有一样

方法二:字符串来表示属性的调用

1
2
3
4
5
6
7
8
9
<script>
export default {
watch: {
"friend.friendName"(newVal, oldVal) {
console.log("string friend:newVal--" + newVal + ",oldVal--" + oldVal);
},
}
}
</script>

方法三:使用computed属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
export default {
watch: {
// 和computed计算属性名一样
friendName(newVal, oldVal) {
console.log("computed friend:newVal--" + newVal + ",oldVal--" + oldVal);
},
},
computed: {
friendName() {
return this.friend.friendName;
},
},
}
</script>

触发监听

1、修改现有对象某个属性对应的值

把对象某个属性的值修改都触发,无论改成基本类型还是引用对象,而且引用对象内vue也能监听到。

1
2
// 例1:数据修改成功,视图修改成功=三种方式的属性监听都触发
this.friend.friendName = "li";

2、属性的添加

对于已经创建的实例,Vue 不允许动态添加根级别(外层内层都监听不到)的响应式 property: 无法渲染
解决方案:

  • 预先留出变量

  • 使用this.$set / Vue.set()

    1
    2
    this.friend.sex = "girl";		// 数据修改但无法渲染 = 监听不到
    this.$set(this.friend, "sex", "boy"); // 可以渲染,deep watch可以监听

    3、属性的删除

    Vue 无法检测 property 的移除
    解决方案:

  • Vue.delete()删除对象某个元素后,会立即触发页面渲染:Vue.delete(propertyName/index)

    1
    2
    delete this.friend.age;	//数据删除但无法渲染 = 监听不到
    this.$delete(this.friend, "age"); // 可以渲染,deep watch可以监听

    三、对于数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <template>
    <div class="about">
    <input type="text" :value="colors" />
    <input type="text" :value="colors[0]" />
    <button @click="changeColors">更新视图</button>
    </div>
    </template>
    <script>
    export default {
    data() {
    return {
    colors: ["red", "orange", "green"]
    };
    },
    }
    </script>

    监听数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <script>
    export default {
    watch: {
    colors(newVal, oldVal) {
    console.log("colors:newVal--" + newVal + ",oldVal--" + oldVal);
    },
    },
    }
    </script>

    触发监听

    1、直接赋值新数组

    1
    2
    // 例子1:修改成功,数组、元素都渲染成功,触发上面对数组的监听
    this.colors = ["1", "2", "3"];

    2、利用索引直接设置一个数组项

    例如:arr[indexOfItem] = newValue;
    解决方案:

  • 使用this.$set(arr, index, newVal)

  • 使用splice(indexOfItem, 1, newValue):

  • 使用临时变量直接赋值的方式,原理与直接赋值数组一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    this.colors[0] = "blue";  // 不是响应的,数据修改成功但渲染不成功 = 不触发监听
    // 方法一:
    this.$set(this.colors, 0, "blue") // 渲染成功 = 触发监听
    // 方法二
    this.colors.splice(0, 1, "blue"); // 渲染成功 = 触发监听
    // 方法三
    let temp = [...this.colors];
    temp[0] = "blue";
    this.colors = temp; //渲染成功 = 触发监听

    3、push数组

    Vue可以监听

    1
    this.colors.push('yellow')	// 数据添加成功,视图渲染成功

    4、修改数组的长度

    例如:arr.length = newLength
    长度大于原数组就将后续元素设置为 undefined, 长度小于原数组就将多余元素截掉。
    解决方案:

  • this.$set(arr, index, newVal);

  • 使用数组 splice 方法可以监听

  • 使用临时变量直接赋值的方式,原理与直接赋值数组一样

    1
    2
    3
    this.colors.length = 1;	// 数据修改成功,视图渲染不成功
    方法一:
    this.colors.splice(1) // 渲染成功 = 触发监听 1之后数组的元素都被删除,包括1