上一篇文章中介紹到了Vue.js裡面的計算屬性(Computed),也比較了Computed與Methods之間的差異,今天再來介紹一個跟他們也有幾許類似的監聽器(Watch),Watch主要是去監聽在Vue裡面的數據,若數據發生變化的時候,會自動去執行相對應的動作,在這邊還是一樣透過BMI計算的範例來說明Watch的運作方法:
<div id="app">
<p>Height:<input type="number" v-model="state.height">CM</p>
<p>Weight:<input type="number" v-model="state.weight">KG</p>
<p>BMI: {{ state.bmi }}</p>
<p>Suggestion:{{ state.bmiMessage }}</p>
</div>
<script>
const { reactive, watch } = Vue;
const app = {
setup(){
const state = reactive({
height: 180,
weight: 80,
bmi: 24.69,
bmiMessage: "過重",
})
watch(() => [state.height, state.weight], () =>{
generateBMI();
})
function generateBMI(){
state.bmi = (state.weight / (Math.pow((state.height / 100), 2))).toFixed(2);
if (state.bmi > 35) {
state.bmiMessage = "重度肥胖";
} else if (state.bmi >= 30 && state.bmi < 35) {
state.bmiMessage = "中度肥胖";
} else if (state.bmi >= 27 && state.bmi < 30) {
state.bmiMessage = "輕度肥胖";
} else if (state.bmi >= 24 && state.bmi < 27) {
state.bmiMessage = "過重";
} else if (state.bmi >= 18.5 && state.bmi < 24) {
state.bmiMessage = "正常範圍";
} else {
state.bmiMessage = "體重過輕";
}
}
return { state };
}
}
const myVue = Vue.createApp(app).mount("#app");
</script>
Watch在上面範例的10~12行,主要是監聽height和weight兩項數值,若數值有變化會自動觸發其中的動作,而從範例中會執行generateBMI Function。
也許這個範例並沒有辦法感受到Watch的作用,因為這個範例同樣可以透過Computed來完成,在這邊就不示範做法了,接下來我們感受看看一些其他的Watch使用情境:
<div id="app">
<p>用戶姓名:
<input type="text" v-model="state.username" placeholder="請輸入5~15個小寫英文,符號僅能使用@-_" size="50">
<span class="errorMessage">{{ state.usernameMsg }}</span>
</p>
</div>
<script>
const { reactive, watch } = Vue;
const app = {
setup(){
const state = reactive({
username: "",
usernameMsg: "",
})
watch(() => state.username, (value) =>{
state.username = state.username.replace(/[^a-zA-Z0-9_@-]/g, "");
const usernameReg = /^.{5,15}$/;
if (usernameReg.test(value)) {
state.usernameMsg = "";
} else {
state.usernameMsg = "請輸入5~15個字以上";
}
})
return { state };
}
}
const myVue = Vue.createApp(app).mount("#app");
</script>
這個範例是一個讓使用者輸入名稱的Input Box,我們希望在輸入資料的時候,限制僅能輸入英文大小寫、數字與“@”、“_”、“-”,這三個特殊符號,只要超過上述的範圍,會讓使用者無法進行輸入,而這樣的條件剛好讓我們可以透過Watch方便的來完成。
我們來思考一樣這樣的案例是否可以透過Computed來做?如果你還記得上一篇文章提到Computed提到的特點,就會發現上面程式中Replace那一行一定會有問題,因為他是要將值回寫到username中,而Computed還需要透過Setter來做那相對來說就麻煩很多,那Function呢?當然以上都是可行的作法,但都不如Watch來得方便,所以沒有什麼是一定要用到誰,而是透過不同的使用情境來決定!
我們把上述的範例進行一些延伸,大家也可以多多思考,有哪些案例透過Watch來處理可以達到事半功倍的效果。
<div id="app">
<p>用戶姓名:
<input type="text" v-model="state.username" placeholder="請輸入5~15個小寫英文,符號僅能使用@-_" size="50">
<span class="errorMessage">{{ state.errorMessage.usernameMsg }}</span>
</p>
<p>電子郵件:
<input type="text" v-model="state.email" placeholder="請輸入電子郵件" size="25">
<span class="errorMessage">{{ state.errorMessage.emailMsg }}</span>
</p>
<p>登入密碼:
<input type="text" v-model="state.password" placeholder="請輸入八個字以上,建議包含大小寫英文字母與數字" size="45">
<span class="errorMessage">{{ state.errorMessage.passwordMsg }}</span>
</p>
<p>密碼確認:
<input type="text" v-model="state.passwordConfirm" placeholder="請再輸入一次密碼" size="45">
<span class="errorMessage">{{ state.errorMessage.passwordConfirmMsg }}</span>
</p>
</div>
<script>
const { reactive, watch } = Vue;
const app = {
setup(){
const state = reactive({
username: "",
email: "",
password: "",
passwordConfirm: "",
errorMessage: {
usernameMsg: "",
emailMsg: "",
passwordMsg: "",
passwordConfirmMsg: "",
}
})
watch(() => state.username, (value) =>{
state.username = state.username.replace(/[^a-zA-Z0-9_@-]/g, "");
const usernameReg = /^.{5,15}$/;
if (usernameReg.test(value)) {
state.errorMessage.usernameMsg = "";
} else {
state.errorMessage.usernameMsg = "請輸入5~15個字以上";
}
})
watch(() => state.email, (value) =>{
const emailReg = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g;
if (emailReg.test(value)) {
state.errorMessage.emailMsg = "";
} else {
state.errorMessage.emailMsg = "無效的電子郵件地址";
}
})
watch(() => state.password, (value) =>{
let passwordLV = 0;
if (!/^.{8,}$/.test(value)) { // 八位數以上
state.errorMessage.passwordMsg = "請輸入八個字以上的密碼";
} else {
if (/\d+/.test(value)) { // 包含數字
passwordLV++;
}
if (/[a-z]+/.test(value)) { // 包含小寫英文
passwordLV++;
}
if (/[A-Z]+/.test(value)) { // 包含大寫英文
passwordLV++;
}
if (passwordLV === 1) {
state.errorMessage.passwordMsg = "弱";
} else if (passwordLV === 2) {
state.errorMessage.passwordMsg = "中";
} else {
state.errorMessage.passwordMsg = "強";
}
}
})
watch(() => state.passwordConfirm, (value) =>{
if (state.password !== value) {
state.errorMessage.passwordConfirmMsg = "與密碼不符";
} else {
state.errorMessage.passwordConfirmMsg = "";
}
})
return { state };
}
}
const myVue = Vue.createApp(app).mount("#app");
</script>
上面的案例大家也可以思考看看,如何透過其他的方法來完成,是否會比Watch更好更快?還是你已經找到Watch的特點了呢?