1. 程式人生 > 遊戲攻略 >《原神攻略》宵宮突破材料鳴草、繪卷收集攻略

《原神攻略》宵宮突破材料鳴草、繪卷收集攻略

監聽器

Vue提供了一種更通用的方式來觀察和響應Vue例項上的資料變動:監聽器。當有一些資料需要隨著其他資料變化而變化時就可以使用監聽器。

使用監聽器

監聽器在Vue例項的選項物件的watch中定義,下面用監聽器實現千米和米的換算:

<html>
	<head>
		<meta charset="UTF-8">
		<title>監聽器</title>
	</head>
	<body>
		<div id = "app">
		   千米 : <input type = "text" v-model="kilometers">
		   米 : <input type = "text" v-model="meters">
		</div>
		<script src="https://unpkg.com/vue@next"></script>
		<script>
			const vm = Vue.createApp({
    		    data() {
    		        return {
    		            kilometers: 0,
    		            meters: 0
    		        }
    		    },
    		    watch: {
    	            kilometers(val) {
    	                this.meters = val * 1000;
    	            },
	                // 監聽器函式也可以接受兩個引數,val是當前值,oldVal是改變之前的值
    	            meters(val, oldVal) {
    	                this.kilometers = val / 1000;
    	            }
		        }
		    }).mount('#app');
		</script>
	</body>
</html>

我們編寫了兩個監聽器,分別監聽資料屬性kilometers和meters的變化,當其中一個數據發生變化時,對應的監聽器就會被呼叫,經過計算得到另一個數據屬性的值。

當需要在資料變化時執行非同步或開銷較大的操作時,使用監聽器是最合適的。例如在一個線上問答系統中,使用者輸入的問題需要到服務端獲取答案,就可以考慮對問題屬性進行監聽,在非同步請求答案的過程中,可以設定中間狀態,向用戶提示“請稍後。。。”,而這樣的功能使用計算屬性就無法做到。

下面用監聽器實現菲波那契數列,斐波那契數列的計算比較耗時,我們採用HTML5新增的WebWorker來計算。

function fibonacci(n){
    return n < 2 ? n :arguments.callee(n-1) + arguments.callee(n-2);
}

onmessage = function(event){
    var num = parseInt(event.data,10);
    postMessage(fibonacci(num));
}

接下來是主頁面:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>監聽器</title>
	</head>
	<body>
		<div id = "app">
		   請輸入要計算斐波那契數列的第幾個數:
			<input type="text" v-model="num">
			<p v-show="result">{{result}}</p>
		</div>
	
		<script src="https://unpkg.com/vue@next"></script>
		<script>
			const vm = Vue.createApp({
    		    data() {
    		        return {
    		            num: 0,
    		            result: ''
    		        }
    		    },
				watch: {
				    num(val) {
    				  	this.result = "請稍候...";
    				  	if(val > 0){
    				    	const worker = new Worker("fibonacci.js");
    				    	worker.onmessage = (event) => this.result = event.data;
    						worker.postMessage(val);
    					}
    					else{
    						this.result = '';
    					}
				    }
				}
			}).mount('#app');
		</script>
	</body>
</html

說明:斐波那契數列比較耗時所以在25行給result資料屬性設定了一箇中間狀態,給使用者一個提示。

worker例項是非同步執行的,當後臺執行緒執行完任務後通過postMessage呼叫通知建立者執行緒的onmessage回撥函式,在該函式中可以通過event物件的data屬性得到資料。在這種非同步回撥的執行過程中,this的指向會發生變化,如果onmessage回撥函式寫成如下形式:worker.onmessage = function(event){this.result = event.data};,那麼在執行onmessage函式時,this實際上指向worker物件,自然this.result就是undefined。如果用箭頭函式,它繫結的是父級作用域的上下文,在這裡就是vm物件。

本例使用了WebWorker,直接在檔案系統開啟會報錯,解決方法是將本例部署到本地的一個Web伺服器中。

監聽器的更多形式

監聽器在定義時除了直接寫一個函式還可以接收一個方法名:

<div id='app'>
    年齡:<input type='text' v-model='age'>
    <p v-if='info'>{{info}}</p>
</div>

<script>
	const vm = Vue.createApp({
        data() {
            return {
                age:0,
                info:''
            }
        },
        methods:{
            checkAge(){
                if(this.age>=18)
                    this.info='已成年';
                else
                    this.info='未成年';
            }
        },
        watch:{
            age:'checkAge'
        }
    }).mount('#app');
</script>

監聽器還可以監聽一個物件的屬性變化:

<div id='app'>
    年齡:<input type='text' v-model='person.age'>
    <p v-if='info'>{{info}}</p>
</div>
<script>
	const vm = Vue.createApp({
        data() {
            return {
                person:{
                    age:0,
                    name:'lisi'
            },
                info:''
        }
        },
        watch:{
            //該回調會在person物件的屬性改變時被呼叫,無論該屬性巢狀多深
            person:{
                handler(val,oldVal){
                    if(val.age>=18)
                        this.info = '已成年';
                    else
                        this.info = '未成年';
                }
                deep:true
            }
        }
    }).mount('#app');
</script>

要注意在監聽物件屬性改變時,使用了handler和deep,前者用於定義當資料變化時呼叫的監聽器函式,後者主要在監聽物件屬性變化時使用,如果為true,表示無論該物件的屬性在物件中的層級有多深,只要該屬性的值發生變化都會被監測。

監聽器函式在初始渲染時並不會被呼叫,只有在後續監聽的屬性發生變化時才會被呼叫,如果要讓監聽器函式在監聽開始後立即執行,可以使用immediate,將其設為true。例如:

 watch:{
     //該回調會在person物件的屬性改變時被呼叫,無論該屬性巢狀多深
     person:{
         handler(val,oldVal){
             if(val.age>=18)
                 this.info = '已成年';
             else
                 this.info = '未成年';
         }
         deep:true,
             immediate:true
     }
 }

當頁面載入後就會立即顯示未成年。

如果僅僅是監聽物件的一個屬性變化,且變化也不影響物件的其他屬性,那麼就沒必要這麼麻煩,直接監聽該物件屬性即可:

watch:{
    //因為有特殊字元點號,所以要用引號包裹
    'person.age':function(val,oldVal){
        ...
    }
}

例項方法$watch

除了在watch選項中定義監聽屬性外,還可以使用元件例項的$watch方法觀察元件例項上的響應式屬性或計算屬性的更改。對於頂層資料屬性、prop和計算屬性,只能以字串的形式傳遞他們的名字。例如:

vm.$watch('kilometers',(newValue,oldValue) => {
    //這個回撥將在vm.kilometers改變後呼叫
    document.getElementById('info').innerHTML = "修改前的值為:" + oldValue + ",修改後的值:" + newValue;
})

對於更復雜的表示式或巢狀屬性,則需要使用函式形式:

const app = Vue.createApp({
    data(){
        return {
            a:1,
            b:2,
            c:{
                d:3,
                e:4
            }
        }
    },
    created(){
        //頂層屬性名
        this.$watch('a',(newVal,oldVal) => {
            //do something
        })
        //用於監聽單個巢狀屬性的函式
        this.$watch(
        () => this.c.d,
        (newVal,oldVal) => {
            //do something
        })
        //用於監聽複雜表示式的函式
        this.$watch(
        //每次表示式“this.a+this.b”產生不同結果時
        //都會呼叫處理程式
        () => this.a + this.b,
        (newVal,oldVal) => {
            //do something
        })
    }
})

$watch方法還可以接收一個可選的選項物件作為其第三個引數。例如。要監聽物件內部的巢狀屬性的變化時,可以在選項引數中傳入deep:true:

vm.$watch('person',callback,{
    deep:true
})
vm.person.age = 20
//回撥函式被觸發