iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Vue.js

新手學習Vue.js與實作之旅系列 第 29

Day29 實作 Quiz (II)

  • 分享至 

  • xImage
  •  

今天的內容將延續昨天的實作內容,繼續探討 Vue 實際的應用/images/emoticon/emoticon30.gif
以下實作是使用 Composition API 撰寫,將著重講解<script setup>標籤的程式碼,以及<template>標籤內有使用到 Vue 語法的地方:

3.點擊圖卡跳轉頁面

  • 安裝 Vue Router
    可以參考 Day24 Vue Router (I) 的內容,或者是在專案的 terminal 輸入 npm install vue-router ,將路由新增到 Vue 應用程式,然後在 src 創建 router 資料夾,並在裡面新增 index.js 檔案,定義 router 規則,之後在 main.js 導入 router 規則。
  • Props
    將 App.vue 的全部程式碼劃分到 QuizesView.vue 檔案中,透過隔離程式碼,以便於修改程式碼、增加重複使用性。
  • Router
    在 Card.vue 檔案中使用 router.push 將某個路徑推送到 URL ,讓使用者點擊圖卡會變更網址。
<!-- Card.vue 檔案 -->
<script setup>
  import { defineProps } from 'vue'
  import {useRouter} from "vue-router"
  
  const {quiz} = defineProps(['quiz'])
  const router = useRouter()
  const navigateToQuiz = () => {
    router.push(`/quiz/${quiz.id}`)
  }
</script>
<template>
    <div class="card" @click="navigateToQuiz">
        <img :src="quiz.img" alt="">
        <div class="card-text">
            <h2>{{ quiz.name }}</h2>
            <p>{{ quiz.questions.length }} questions</p>
        </div>
    </div>
</template>

4.渲染題目畫面

創建 QuizView.vue 檔案並寫入題目內容,然後在 index.js 寫入 QuizView 的路由規則。

// index.js 檔案
import { createRouter, createWebHistory} from "vue-router"
import QuizesView from "../views/QuizesView.vue"
import QuizView from "../views/QuizView.vue"

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes: [
        {
            path:"/",
            name:'quizes',
            component: QuizesView
        },
        {
            path:"/quiz/:id",
            name:"quiz",
            component: QuizView
        }
    ]
})

export default router
  • Props

動態Props:使用 v-bind (或簡寫:) 的方式將外層元件的資料內容傳入

將 QuizView.vue 的題目程式碼劃分到 Question.vue 和 QuizHeader.vue 檔案中,透過隔離程式碼,以便於修改程式碼、增加重複使用性。

  • Route
    使用 useRoute() 取得目前路由物件。
  • computed
    根據響應式資料自動計算值,當 currentQuestionIndex 發生改變時,會自動更新 questionStatus 和 barPercentage。
<!-- QuizView.vue 檔案 -->
<script setup>
    import Question from "../components/Question.vue"
    import QuizHeader from "../components/QuizHeader.vue"
    import {useRoute} from "vue-router"
    import {ref, computed} from "vue"
    import  quizes from "../data/quizes.json"

    const route = useRoute()
    const quizId = parseInt(route.params.id)
    const quiz = quizes.find(q => q.id === quizId)
    const currentQuestionIndex = ref(0)
    
    const questionStatus = computed(() => 
    `${currentQuestionIndex.value}/${quiz.questions.length}`)
    const barPercentage = computed(() => 
    `${currentQuestionIndex.value/quiz.questions.length*100}%`)

</script>

<template>
    <div>
        <QuizHeader 
            :questionStatus="questionStatus"
            :barPercentage="barPercentage"
        />
        <div>
            <Question 
            :question="quiz.questions[currentQuestionIndex]"
            />
        </div>
    </div>
</template>
  • 列表渲染 v-for
    透過遍歷來顯示所有題目選項內容,並且搭配 key 屬性當作唯一識別碼。
  • 屬性綁定 v-bind (簡寫為 :)
    透過 key 屬性綁定 option.id 作為 option 的唯一識別碼,讓 Vue 能夠高效追蹤每個 option 元素。
  • 插植表達式 {{ }}
    透過使用插植表達式來渲染題目內容。
<!-- Question.vue 檔案 -->
<script setup>
    import {defineProps} from "vue"
    const {question} = defineProps(['question'])
</script>
<template>
    <div class="question-container">
        <h1 class="question">
            {{ question.text }}
        </h1>
    </div>
    <div class="options-container">
        <div 
            v-for="option in question.options" 
            :key="option.id"
            class="option">
            <p class="option-label">{{ option.label }}</p>
            <div class="option-value">
                <p>{{ option.text }}</p>
            </div>
        </div>
    </div>
</template>
  • 插植表達式 {{ }}
    透過使用插植表達式來渲染完成題數。
  • 屬性綁定 v-bind (簡寫為 :)
    透過 style 屬性來動態設定進度條的長度,進行動態樣式綁定。
<!-- QuizHeader.vue 檔案 -->
<script setup>
    import { defineProps } from 'vue'
    const {questionStatus, barPercentage} = 
    defineProps (["questionStatus","barPercentage"])
</script>
<template>
    <div>
        <header>
            <h4>Question {{ questionStatus }}</h4>
            <div class="bar">
                <div class="completion" :style="{ width: barPercentage }"></div>
            </div>
        </header>
    </div>
</template>

<style scoped>
    header{
        margin-top: 20px;
    }
    header h4{
        font-size: 30px;
    }
    .bar{
        width: 300px;
        height: 50px;
        border: 3px solid rgb(40, 126, 28);
    }
    .completion{
        height: 100%;
        width: 0%;
        background-color: rgb(97, 167, 98);
    }
</style>

參考資源

https://www.youtube.com/watch?v=I_xLMmNeLDY


上一篇
Day28 實作 Quiz (I)
下一篇
Day30 實作 Quiz (III)
系列文
新手學習Vue.js與實作之旅30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言