官方解釋:> 觀察 Vue 實例變化的一個表達式或計算屬性函數。回調函數得到的參數為新值和舊值。表達式只接受監督的鍵路徑。對于更復雜的表達式,用一個函數取代<br/>## 1. 偵聽器的基本使用偵聽器可以監聽data對象屬性或者計算屬性的變化watch是觀察屬性的變化所以watch的屬性名必須要與觀察人的名字保持一致;只要觀察的值發生了變化才會觸發,```html
<div id="app"><!-- 監聽器 --><input type="text" v-model="msg">
</div><script> const vm = new Vue({el: "#app",data: {msg:""},watch:{msg(){console.log("數據發生了變化")console.log(arguments)}}})
</script>
通過這個理解,我們就會發現, 只要數據一但發生變化,那么監聽函數msg就會被觸發, 監聽函數中接受兩個參數,第一個參數是數據變化后的新值, 第二個參數是數據變化后的舊值
盡管大部分時間我們用不到偵聽器, 但偵聽器對于處理異步操作非常適合,
例如我們需要將用戶輸入的內容延遲5秒后現在在頁面上
<div id="app"><!-- 監聽器 --><input type="text" v-model="msg">{{showMsg}}
</div><script> const vm = new Vue({el: "#app",data: {msg:"",showMsg: ""},watch:{msg(){let newValue = this.msgsetTimeout(() => {this.showMsg = newValue},5000)}}})
</script>
2. 獲取舊值
偵聽器在數據發生變化的時候就會觸發,觸發時,數據已經更新,我們那到就是新值,那么我們如何獲取之前的舊值呢
其實當監聽的屬性發生變化時,偵聽器會被傳入兩個參數
第一個參數:偵聽器所監聽屬性的當前值,即更新后的值
第二個參數: 原來舊值
<div id="app"><!-- 監聽器 --><input type="text" v-model="msg">
</div><script> const vm = new Vue({el: "#app",data: {msg:"",},watch:{msg(val, oldval){console.log(val);console.log(oldval); }}})
</script>
3. 監聽data對象中某個對象的屬性
data屬性中的數據值除了是基本數據類型的數據外,還有可能是對象類型,那么我們如何監聽對象數據的屬性的
<div id="app"><!-- 監聽器 --><input type="text" v-model.number="fruit.price"></div><script> const vm = new Vue({el: "#app",data: {fruit:{name:"蘋果",price: 20},},watch:{fruit(val, oldval){console.log(val);console.log(oldval); }}})
</script>
如果我們按照之前的監聽方式, 那么我們就會發現,當我們修改fruit屬性值的時候,偵聽器不會被觸發, 偵聽器會在fruit對象整體被修改時觸發.
為了監聽對象里某個特定屬性的變化,可以在偵聽器的名稱中使用.
操作符, 就像訪問這個對象的屬性
<div id="app"><!-- 監聽器 --><input type="text" v-model.number="fruit.price"></div><script> const vm = new Vue({el: "#app",data: {fruit:{name:"蘋果",price: 20},},watch:{"fruit.price"(val, oldval){console.log(val);console.log(oldval); }}})
</script>
4. 深度監聽
通過上面的例子,我們知道,我們可以監聽對象的特定屬性的變化,可以我們想監聽整個對象的所有屬性的變化就需要給對象所有的屬性添加監聽就不是特別的好,如果我們只是單純的監聽對象,那么屬性的變化并不會觸發監聽器,只有整個對象被替換時才會觸發
所以我們可以通過deep屬性來開啟對象的深度監聽,
4.1 deep 選項
為了發現對象內部值的變化,可以在選項參數中指定 deep: true
。注意監聽數組的變更不需要這么做。
如果需要開啟深度監聽,那么監聽器將不再是一個函數,而需要寫成一個對象,對象中配置deep屬性
<div id="app"><!-- 監聽器 --><input type="text" v-model.number="fruit.price"></div><script> const vm = new Vue({el: "#app",data: {fruit:{name:"蘋果",price: 20},},watch:{fruit:{// 此時fruite 就是一個配置對象,里面的屬性都是配置選項// handler 就是原來的監聽函數, 當數據變化是執行的函數handler(val,oldval){console.log(val);console.log(oldval); },// 深度監聽選項deep:true}}})
</script>
此時我們就做到了即監聽這整個對象的變化, 也簡體對象里面所有的屬性的變化,
4.2 immediate選項
監聽除了deep選項外,還有immediate
選項
指定 immediate: true
將立即以表達式的當前值觸發回調,
watch:{fruit:{// 此時fruite 就是一個配置對象,里面的屬性都是配置選項// handler 就是原來的監聽函數, 當數據變化是執行的函數handler(val,oldval){console.log(val);console.log(oldval); },// 深度監聽選項deep:true,immediate: true // 理解執行監聽函數handler}
}
5. 引用類型深度監聽后,屬性變化,獲取新舊值問題
但是細心的朋友就會發現我們在改變對象屬性的時候,雖然觸發了偵聽器,但是我沒發獲取舊值了,我們拿到的兩個形參的值都是對象更改后的新值.
出于某種原因沒有深入過濾對象的每個屬性,那么只能監聽到對象的變化,而JavaScript里對象的賦值是引用賦值,雖然屬性變化了,但是它引用的地址卻一直沒有變化,這樣的話,當對象的屬性值改變了,Vue雖然知道它改變了,但也只能循著引用地址去獲得對象,可此時對象的屬性的值已經改變了,因此Vue并不能得到變異之前的值。
示例:
<div id="app"><!-- 監聽器 --><input type="text" v-model.number="fruit.price"></div><script> const vm = new Vue({el: "#app",data: {fruit:{name:"蘋果",price: 20},},watch:{fruit:{handler(val,oldval){console.log(11)console.log(val);console.log(oldval); },deep:true}},})
</script>
此時當數據發生變化是, 查看handler 兩個參數值
5.1 解決方案: 利用計算屬性
官方方案: 觀察 Vue 實例變化的一個表達式或計算屬性函數。回調函數得到的參數為新值和舊值。表達式只接受監督的鍵路徑。對于更復雜的表達式,用一個函數取代
既然 watch
無法在變異對象或數組時監聽新舊值,那么我們可以先使用JSON.parse()
來淺復制一遍data對象,然后在復制的對象上修改,完了重新賦值給該data對象,這樣變化前后兩個對象是完全不一樣的,因為它們的引用地址完全不一樣,Vue可以循著兩個引用地址獲得新舊兩個
<div id="app"><!-- 監聽器 --><input type="text" v-model.number="fruit.price"></div><script> const vm = new Vue({el: "#app",data: {fruit:{name:"蘋果",price: 20},},watch:{fruitNew:{handler(val,oldval){console.log(11)console.log(val);console.log(oldval); },deep:true}},computed:{fruitNew(){return JSON.parse(JSON.stringify(this.fruit));}},})
</script>
6. 可以通過Vue是實例對象的$watch屬性來監聽
除了 watch
選項之外,您還可以使用命令式的 vm.$watch ,通過Vue實例對象來監聽數據
6.1 普通監聽
可以通過實例對象調用$watch設置監聽
<!-- 監聽字符變化-->
<div id="app"><!-- 監聽器 --><input type="text" v-model="msg"></div><script> const vm = new Vue({el: "#app",data: {msg:'你好'}})// $watch 是一個實例方法vm.$watch("msg",(val,newVal) => {console.log(val)console.log(newVal); })
</script>
6.2 監聽配置
<div id="app"><!-- 監聽器 --><input type="text" v-model.number="fruit.price">
</div><script> const vm = new Vue({el: "#app",data: {fruit:{name:"蘋果",price: 20`在這里插入代碼片`},}})// $watch 是一個實例方法vm.$watch("fruit",(val,newVal) => {console.log(val)console.log(newVal); },{deep: true})
</script>