如果阅读有疑问的话,欢迎评论或私信!!
文章目录
侦听器
前言
在computed计算属性中,我们知道如果需要计算"衍生值":通过监听已有的响应式数据,得到一个新的属性
。但是在计算属性中,vue不推荐直接修改属性的值,或者使得其他DOM变化,那么如果我们确实需要产生一些使其他DOM变化时,vue为我们提供了watch侦听器。
基本示例
为了页面更加简介,在演示前先将下面案例中会反复使用的getAnswer函数
贴在这里:
html
<script>
methods: {
async getAnswer() {
this.loading = true //禁止input输入框输入新的数据
this.answer = 'Thinking...'
try {
const res = await fetch('https://yesno.wtf/api')
this.answer = (await res.json()).answer
} catch (error) {
this.answer = 'Error! Could not reach the API. ' + error
} finally {
this.loading = false //处理完成后允许input输入新数据
}
}
}
</script>
使用属性值表示watch值
我们使用watch侦听器时,只需要像computed计算属性一样将方法名绑定在DOM上。例如:
html
<template>
<p>Ask a yes/no question:
<input v-model="question" :disabled="loading" />
</p>
<p>{{ answer }}</p>
</template>
<script>
export default {
data() {
return {
question: '', //将question声明为属性值,不声明的话在watch中无法监听
answer: 'Questions usually contain a question mark. ;-)',
loading: false //设置input的disabled属性,初始值可输入
}
},
watch: {
// 每当 question 改变时,这个函数就会执行
question(newQuestion, oldQuestion) { //监听question属性的变化
if (newQuestion.includes('?') && newQuestion!=oldQuestion) {
// watch中可以监听newQuestion新值和oldQuestion旧值属性
//当question中输入新的值时,调用getAnswer方法
this.getAnswer()
}
}
},
}
</script>
在该案例中,是一个很好的根据DOM中值变化时修改其他DOM中的值
的案例。
使用路径表示watch值
同时,watch选项支持把watch中的键设置为用.
分隔的路径(即对象嵌套中的路径),可以将上方的代码这样写:
html
<template>
<p>Ask a yes/no question:
<input v-model="some.nested.question" :disabled="loading" />
</p>
<p>{{ answer }}</p>
</template>
<script>
export default {
data() {
return {
some:{
nested:{
question:'',
}
}, //将question声明为属性值,不声明的话在watch中无法监听
answer: 'Questions usually contain a question mark. ;-)',
loading: false //设置input的disabled属性,初始值可输入
}
},
watch: {
// 每当 question 改变时,这个函数就会执行
'some.nested.question'(newQuestion, oldQuestion) { //监听question属性的变化
if (newQuestion.includes('?') && newQuestion!=oldQuestion) {
// watch中可以监听newQuestion新值和oldQuestion旧值属性
//当question中输入新的值时,调用getAnswer方法
this.getAnswer()
}
}
}
}
</script>
深层侦听器
watch
默认是浅层的:被侦听的属性,仅在被赋新值时,才会触发回调函数------而嵌套属性的变化不会触发。如果想侦听所有嵌套的变更,你需要深层侦听器。
拿上述场景举例:如果监听的是nested而不是question,那么如果只是修改quesiotn值是不会触发监听事件,只有修改整个nested值才可以触发。例如:
html
<template>
<p>Ask a yes/no question:
<input v-model="some.nested.question" :disabled="loading" />
<button @click="some.nested.question = 'b?'">点我</button>
</p>
<p>{{ answer }}</p>
</template>
<script>
export default {
data() {
return {
some:{
nested:{
question:'',
}
}, //将question声明为属性值,不声明的话在watch中无法监听
answer: 'Questions usually contain a question mark. ;-)',
loading: false //设置input的disabled属性,初始值可输入
}
},
watch: {
// 每当 question 改变时,这个函数就会执行
'some.nested'(newQuestion, oldQuestion) { //监听question属性的变化
if (newQuestion.includes('?') && newQuestion!=oldQuestion) {
// watch中可以监听newQuestion新值和oldQuestion旧值属性
//当question中输入新的值时,调用getAnswer方法
this.getAnswer()
}
}
}
}
</script>
在上面代码中,我们不应该使用newQuestion.includes('?'),因为这里newQuestion是一个对象,而不是字符串。但是我们运行会发现并没有报错,也就是根本没有引起侦听。
在未使用deep深层监听器时,我们修改input值不会引起watch的变化。当修改nested整个对象时才会引起侦听事件,修改button按钮:
html
<button @click="some.nested = 'b?'">点我</button>
修改button之后我们要注意这里会抛出一个异常,这是正常的,因为我们这个代码需要使用includes('?'),而newQuestion是一个对象。由此我们也可以确定确实出发了监听事件。
在修改button之后才会引起侦听事件,那么如果想要不修改整个对象引起侦听就要使用deep,例如:
html
<template>
<p>Ask a yes/no question:
<input v-model="some.nested.question" :disabled="loading" />
<button @click="some.nested.question = 'b?'">点我</button>
</p>
<p>{{ answer }}</p>
</template>
<script>
export default {
data() {
return {
some:{
nested:{
question:'',
}
}, //将question声明为属性值,不声明的话在watch中无法监听
answer: 'Questions usually contain a question mark. ;-)',
loading: false //设置input的disabled属性,初始值可输入
}
},
watch: {
// 每当 question 改变时,这个函数就会执行
'some.nested'(newQuestion, oldQuestion) { //监听question属性的变化
if (newQuestion.question.includes('?')) {
// watch中可以监听newQuestion新值和oldQuestion旧值属性
//当question中输入新的值时,调用getAnswer方法
this.getAnswer()
},
deep:true
}
}
}
</script>
在该代码中,可以正常监听some.nested的变化。但是需要注意的是我们这里删除了newQuestion!=oldQuestion
,因为官方文档中提到了:
// 注意:在嵌套的变更中,
// 只要没有替换对象本身,
// 那么这里的
newValue
和oldValue
相同
可以自己尝试添加之后在该代码前进行console进行调试。
即时回调的侦听器
watch
默认是懒执行
的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。
例如上面场景下,我们想在代码执行之前先运行a?
,那么我们可以使用和deep同样的属性immediate: true
:
html
<script>
watch: {
// 每当 question 改变时,这个函数就会执行
"some.nested": {
handler(newQuestion, oldQuestion) {
if (newQuestion.question.includes("?")) {
this.getAnswer();
}
},
immediate:true
},
},
</script>
回调函数的初次执行就发生在 created
钩子之前。Vue 此时已经处理了 data
、computed
和 methods
选项,所以这些属性在第一次调用时就是可用的。关于生命周期到后面会详细解释。
一次性侦听器
和immediate
和deep
类似的属性还有once
,它设置该侦听器的执行次数,例如:
html
watch: {
// 每当 question 改变时,这个函数就会执行
"some.nested": {
handler(newQuestion, oldQuestion) {
if (newQuestion.question.includes("?")) {
this.getAnswer();
}
},
once: true
},
},
</script>
在侦听一次变化之后就不会再侦听后续的变化。