超簡單學習 Vue.js 系列 | Vue tsmc 常用英文單字測驗練習
2022-05-07
Vue.js 簡單的示範專案,使用 Vue CLI 實作 tsmc 常用英文單字測驗練習,結合 ES6 語法特性活用 Array 與 Object,嘗試使用 Html5 Audio Element 用按鈕音效讓使用體驗更佳,並且重構原本的 Vue App。
TSMC 常見英文單字測驗練習 Vue Multiway Buttons
Html 5 Audio
import successSound from '@/assets/success.mp3';
```vue
export default {
data() {
return {
audioSuccess: new Audio('/success.mp3'),
audioFail: new Audio('/fail.mp3'),
};
},
}
async playAudio(bool) {
if (bool === true) {
this.audioSuccess.play();
} else {
this.audioFail.play();
}
},
this.audioSuccess?.paused
在本次的開發,嘗試了使用 Audio 播放音效的方式,在答對或者答錯題目時,會有不同的音效,在 PC 瀏覽器環境測試一切符合預期;但在 Mobile 上,答題過程偶爾會因為音效的播放,整個畫面凍結住,約 10 秒以後才會恢復,讓整個使用者體驗非常不好。因此雖然試驗了播放音效,但在實際的成果仍不加上,並且留待未來突破這個問題。
ES6 Object & Arrays
在設計隨機問題、隨機答案選項的時候,一開始用了好幾個變數來分別代表,但最後卻發現邏輯之間會有問題,當題目數小於需要被抽出的答案數時,會發生 Bug。於是開始思考如何重構原本的邏輯設計。
但在實作的時候,卡關好幾個地方,後來讓頭腦交替在專注與發散模式後,終於釐清是對於 JS Object & Arrays 的使用不夠熟悉 (因為 Python 的資料結構物件用得太過癮,對於其他的語言都會卡卡的 😗)
於是特別開 Node.js 來自問自答,深入認識 JS 的 Object 與 Arrays,以下是過程中的收穫:
let data = require('./quesbank.json')
而 Json 的格式預設為花括號物件,如果是清單類型,需要先指定一個 Properties 再搭配 Array,或者用巢狀花括號物件 (Nested Object)的方式表示。
Nested Object
{
"Alice": { "age": 21},
"Bob": { "age": 23},
"Charlie": { "age": 22}
}
Properties with Array
{
"users": [
"Alice": { "age": 21},
"Bob": { "age": 23},
"Charlie": { "age": 22}
]
}
在製作資料的時候,筆者喜歡用 Nested Object 的方式,但在 JS 的功能實現上,會需要 Array Method 的支援,所以還是會將 Nested Object 進行轉型為 Array:
Object.values(data)
轉型後就可以使用多元且方便的 Array Method 進行處理,包括 Filter
、Map
、Every
、Some
以及 Reduce
。
相關語法的介紹可以參考 ES20XX Modern JS 新語言特性筆記 | Array
重新熟悉 JS 的 Object 以及 Array 後,原本要解決的抽取隨機問題、隨機答案選項的困難,最後化約到在原本的 Json 資料物件上用一個狀態屬性來加工,問題被分成三個狀態,undefined、true 以及 false。
state | meaning |
---|---|
undefined | 尚未回答的問題 |
true | 答對的問題 |
false | 答錯的問題 |
將所有的物件加上狀態屬性 (state):
Object.values(data).forEach(e => e['state'] = undefined)
例如,找出尚未回答過的問題:
Object.values(data).filter(e => e['state'] !== undefined)
如此一來就不需要額外用變數來記錄相關的資訊,重新進行練習也只需要搭配 delete
來清空所有的 Object 狀態 Property 即可。
Object.values(data).forEach(e => delete e['state'])
Refactor
loadAllQues
loadAllQues() {
this.wrongs = new Set();
Object.values(quesbank).forEach((e) => delete e.state);
this.allQues = Object.values(quesbank);
this.getQues();
},
getQues
getQues(data) {
stateCount = _ => Object.values(data).filter(e => e['state'] !== undefined).length
while (stateCount() < Object.values(data).length)
{
console.log('Quest Round');
pick = chance.pickone(
Object.values(data).filter(e => e['state'] === undefined))
sets = chance.pickset(
Object.values(data).filter(e => e !== pick), 3)
console.log(chance.shuffle([pick, ...sets]));
pick['state'] = chance.pickone([true, false])
console.log(data);
}
}
CheckAns
checkAns(i) {
if (this.isQuesRoundDone() !== true) {
const isCorrect = this.ansPools[i] === this.quesObj.meaning;
if (isCorrect) {
this.completed += 1;
this.quesObj.state = true;
} else {
this.quesObj.state = false;
}
this.playAudio(isCorrect === true);
this.clickAns = i;
this.showAns = true;
updateStorage(this.completed);
}
},