原文 [https://michaelnthiessen.com/how-to-watch-nested-data-vue/]
本文解决:
- watch 与 computed 应该使用哪个;
- watch中绑定object或array时,数据没有更新(deep);
- prop的默认值(default)没有触发watch函数(immediate);
- 多个prop更新时触发同一个处理函数
watch 方法是什么
在Vue中,我们可以[监视属性何时更改]并对响执行一些操作。
例如,如果colour
发生变化,我们console一些文字:
export default {
name: 'ColourChange',
props: ['colour'],
watch: {
colour()
console.log('The colour has changed!');
}
}
}
这些观察者使我们能够做各种有用的事情。
但是很多时候,我们本来只需要使用computed实际上却使用率watch。
应该使用watch还是computed?
watched通常会与computed
属性混淆,因为它们的运作方式相似。知道何时使用哪一个更难。
但是我想出了一个很好的经验法则。
改变state使用computed,需要副作用使用watch。
任何异步或发生在component外的,都算是副作用
常见的例子有:
- 请求接口获取数据
- 操作DOM
- 使用浏览器API,例如local storage 或 audio playback
这些不会直接影响组件的被认为是副作用。
如果不执行此类操作,需要响应其他变化而更新计算时,computed确实非常有用。
但有时,computed没什么意义。比如渲染的话
data
就够了。如果需要更新它以响应属性更改,则需要使用观察程序。
注意:使用watch
更新状态时要小心。这意味着您的组件和父组件都在直接或间接地更新相同的状态。This can get very ugly very fast
观察嵌套数据—数组和对象
如果已经确定你需要使用watch而不是computed,但是wach 一个数组或一个对象没能按预期工作。
为什么?
假设有一个数组数组:
const array = [1, 2, 3, 4];
// array = [1, 2, 3, 4]
现在,通过push来更新数组:
array.push(5);
array.push(6);
array.push(7);
// array = [1, 2, 3, 4, 5, 6, 7]
问题是:array
变了吗?
虽然array的内容变了,但是这个array变量的指针指向的内存地址并没有变,所以这个array容器并没有变化。因此,当watch一个数组或对象时,Vue不知道您已经更改了array或object 内部的 内容。您必须告诉Vue在观察更改时希望它需要监控容器的内部。
您可以通过在watch上将设置deep
为true
并写handler 方法来实现它。
export default {
name: 'ColourChange',
props: {
colours: {
type: Array,
required: true,
},
},
watch: {
colours: {
// This will let Vue know to look inside the array
deep: true,
// We have to move our method to a handler field
handler()
console.log('The list of colours has changed!');
}
}
}
}
现在,Vue能够监控容器的内部的内容了。
immediate
观察者仅在prop的值更改时才会触发,但我们通常也需要在启动时触发一次。
假设我们有一个MovieData
组件,它根据movie
prop设置服务器获取数据的地址:
name: 'MovieData',
props: {
movie: {
type: String,
required: true,
}
},
data() {
return {
movieData: {},
}
},
watch: {
// Whenever the movie prop changes, fetch new data
movie(movie) {
// Fetch data about the movie
fetch(`/${movie}`).then((data) => {
this.movieData = data;
});
}
}
}
每当movie
更新时,watcher就会触发,并获取新数据。
但是这里还有一个小问题。当页面加载时,movie
会被设置默认值。但是由于prop还没有改变,所以watch没有被触发。这意味着movie更新之前,不会加载数据。
那么,如何让我们的watch在页面加载后立即触发?
我们将其设置immediate
为true,然后将需要触发的函数放在handler中:
watch: {
// Whenever the movie prop changes, fetch new data
movie: {
// Will fire as soon as the component is created
immediate: true,
handler(movie) {
// Fetch data about the movie
fetch(`/${movie}`).then((data) => {
this.movieData = data;
});
}
}
}
好的,现在我认为是时候介绍一下handler
了。
handler
Watchers有三个不同的属性:
immediate
deep
handler
我们只是看了前两个,第三个也不难。您可能已经在使用它,而没有意识到它。
在prop变更时将会调用handler
方法
如果如果不需要immediate
或者是deep
,我们一般不写如下的代码:
watch: {
movie: {
handler(movie) {
// Fetch data about the movie
fetch(`/${movie}`).then((data) => {
this.movieData = data;
});
}
}
}
而是使用它的简写形式:
watch: {
movie(movie) {
// Fetch data about the movie
fetch(`/${movie}`).then((data) => {
this.movieData = data;
});
}
}
最酷的是Vue还允许我们使用String
来命名处理函数。如果我们想在两个或更多props发生变化时写处理函数,这很有用。
以我们的movie组件示例为例,假设我们同时基于movie
和actor
两个prop来获取数据:
watch: {
// Whenever the movie prop changes, fetch new data
movie {
handler: 'fetchData'
},
// Whenever the actor changes, we'll call the same method
actor: {
handler: 'fetchData',
}
},
methods: {
// Fetch data about the movie
fetchData() {
fetch(`/${this.movie}/${this.actor}`).then((data) => {
this.movieData = data;
});
}
}