初心者教學:使用 Vue CLI 打造 Vue App 前端應用服務


  1. 說明
    1. 環境準備
    2. 專案架構
    3. 動手改寫 Components
      1. v-for & v-on:key
      2. element focus & Watch
    4. 元件之間的溝通
    5. 使用 Router 組織頁面
      1. Hash mode vs Html5 mode
  2. 專案發布
  3. 成果
  4. 小結
  5. 參考資料

以往要寫玩具都是用 Python 來寫,週末在玩雙點醫院的過程中,突然想要寫一個訓練色彩辨別的互動玩具(可能是雙點醫院褪色症的啟發)。同時不想再用 jQuery DOM 操作的方式來寫,但想到對於前端各種工具的熟悉度非常有限,如果要從環境開始準備,並且規劃整個開發流程可能會讓整個專案裹足不前 😑

此時想到 Visual Studio IDE 所帶來的良好開發體驗,何不嘗試以 Vue CLI 的方式建構整個專案,而不要從開發環境開始琢磨,讓時間集中在功能的實踐,忽略環境的細節並且在需要的時候才去搜尋,於是就開始了打造第一個 Vue App 的過程 ✈️

logo

說明

本次使用的是 Vue CLI 4.5.13,開發過程無包袱也無懸念的選擇 Vue 3.x,但選擇之後才發現 3.x 不再支援印象中好用的功能 filter,但影響不大有非常多的替代方式。而用關鍵字 Vue CLI 可以找到 Kuro Hsu大大的 重新認識 Vue.js,對於曾經隨興翻讀 Vue.js 文件並且參加過 Vue.js 線下課程卻沒有實際動手過的作者而言,確實是重新認識 Vue.js 😅

這本電子書是實體書 重新認識 Vue.js:008天絕對看不完的 Vue.js 3 指南 的電子版,提供了實體書前四個章節,包含 Vue.js基礎入門、元件系統、單一元件檔與 Vue CLI 以及 Vue Router。Vuex 以及 Vue Composition API 的部分必須要購買實體書才能夠閱讀。但電子版的內容就豐富到,簡直是佛心來的,同時前四章的內容對於要打造本次的專案已經是綽綽有餘的參考內容。

環境準備

再決定要使用 Vue CLI 進行開發後,只要確認已經安裝 Node.js,本次開發的環境使用的是 node.js v12.2.0,另有安裝 nvm 可以視情況安裝其他 node.js 版本。

node -v
rem v12.2.0

接著就可以使用 npm 下列指令進行安裝 Vue CLI

npm install -g @vue/cli

完成後接著使用開始建立專案,過程中會引導環境設置,這邊也不深入各功能的用途,選擇必要的功能即可,查詢後有發現事後要再增減環境配置相當容易。


完成專案建置後,首先執行確認服務有正常啟動,並且開啟 Visual Studio Code 作為開發過程使用的 Editor。

npm run serve

確認 server 有正常運作後,就可以開始 Coding,而 Coding 的過程會同步反映到瀏覽器中,不需要額外的中斷編譯或者重新整理瀏覽器,開發體驗非常好。

預設執行 run serve 會同時 enable localhost 以及用本機的 IP 去提供其他裝置連線,如果只想要保持 localhost,可以在專案根目錄加上 vue.config.js,並加入下列設定:

module.exports = {
  devServer: {
    host: 'localhost',
    proxy: 'http://localhost:8080'
  }
}

專案架構

首先映入眼簾的是 Vue CLI 所協助建立的專案架構,初步瞄了一下可以斷定開發的工作只要是集中在 src 資料夾下進行,並且編輯的重點就是 Vue extension 的檔案。稍微閱讀一下電子書,確認這種檔案格式稱為 Single File Components, SFC,結構上包含: Template、Script、Style。設計的方式就是設計各式 SFC Vue Components 並且組合再一起構成應用程式。

圖片來源:https://v3.vuejs.org/


檔案 用途
package.json
package-lock.json node.js
管理依賴模組以及定義執行 Script 的設定檔案
main.js 整個 App 的程式邏輯進入點
public/index.html 網頁瀏覽器的所需要的靜態頁面檔
router.js Vue-Router 設定檔
.eslintrc.js 設定 eslint 的規則 (專案使用 airbnb 設定)

動手改寫 Components

一開始先嘗試調整 Template 及 Style,可以發現 Style 有 Scoped 所以元件之間的樣式不會互相影響,同時 Scoped 可以指定要使用的 CSS preprocessor 語言,例如 scss, less, stylus 等等。

<style scoped lang="scss">
.section-question {
  background-color: skyblue;
  width: 100%;
  margin: 0 auto;
  padding: 10px 0;
  font-size: 1.5rem;
}
</style>

但目前對 scss 不太熟悉,所以還是習慣寫一般的 css 只有特定如巢狀關係會使用上。

而對於 template 因為有使用過 handlebars.js 以及使用 razor 等經驗,很容易理解 template 的使用方式,同樣地 template 與可以使用 lang 例如 pug,但因為沒有特別的需求所以本次的專案暫無使用。

接著在實作畫面的過程中,可以往回翻電子書,看一下 Vue Directive 的介紹,隨著畫面的實作,逐步理解從 v-text, v-bind, v-model 到 v-on 的使用時機,不同於以往對於 DOM 的操作,Vue 看到的是元件之間的資料狀態變化關係

v-for & v-on:key

v-for with Key binding

使用 v-for 時要搭配 Key bind 才能夠渲染物件,而 v-for 會為了減少 Virtaul DOM 的資源,而重複使用物件,如果 Key 的綁定不正確會出現 Bug 。

專案中的 AnswerButton 需要重新渲染,但在物件狀態的使用方式下,變成調整 Key 綁定來產生新的物件,而為了避免 Key 值重複,每一次的更新都遞增 round 值作為 Key 的一部分。

<template>
  <AnswerButton v-for="(color, index) in jamming" 
    :key="round+color+index" 
    :color="color" @updatemsg="updatemsg"/>
</template>
export default {
  name: "AnswerOptions",
  data() {
    return {
      round: 0,
    }
  },
  methods: {
    next(){
      this.round += 1
    }
  }
};

element focus & Watch

處理 Input Element 的時候想要使用者能夠輕易地連續輸入資料。所以目標自動 Foucs 在 Input Element 上,藉由 HTML 原生的 autofocus 即可以達到效果。

<input
    class="input-answer"
    type="number"
    ref="inputAnswer"
    pattern="\d*"
    v-model.number="userAnswer"
    v-on:keyup.enter="updateQuestion()"
    autofocus
/>

而一開始是想藉由 Mobile Device Numpad 來連續執行,但發現 iOS Numpad 沒有 Submit Button,後來決定採 Watch 監測的方式當使用者的輸入內容改變時,就進行檢查並將重新聚焦的動作寫在檢查內容的函式,Watch 扮演結合的角色。

設定上 template 需要設定 ref 名稱,才可以由 methods 中透過 refs 去進行 focus。

export default {
  name: 'Home',
  data() {
    return {
      userAnswer: '',
    };
  },
  watch: {
    userAnswer() {
      this.updateQuestion();
    },
  },
  methods: {
    updateQuestion() {
      if (this.answer === this.userAnswer) {
        // logic about update question
        this.$refs.inputAnswer.focus();
      }
    },
  },
}

元件之間的溝通

關鍵心法就是 Props In, Events Out,在想要控制子元件的時候,常會想起 jQuery 控制 DOM 的好,但千萬要忍耐住,並且改以物件狀態的觀點來重新進行設計上的思考。翻閱電子書可以提供靈感與相法,而撐過使用 DOM 的渴望,就能夠逐漸適應 Vue.js Components 式的開發方式。

而在做元件之間的溝通,由父元件以傳入子元件的 Props,而子元件則是以 Events 的方式呼叫父元件的方法來進行狀態更新、資料操作。設計概念上是各元件的資料由元件本身進行管理,才不會造成 Debug 追蹤上的困難。

圖片來源:https://v3.vuejs.org/圖片來源:https://v3.vuejs.org/

父元件 Template

將 updatemsg methods 傳入子元件。

<AnswerButton v-for="(color, index) in jamming" 
  :key="round+color+index" 
  :color="color" @update="updatemsg"/>

父元件 Script

定義 updatemsg 的用途。

export default {
  methods: {
    updatemsg(e) {
      if (e == this.option)
      {
        this.message = `${e} 🎨`
        this.correct = true
      }
      else
      {
        this.message = ''
        this.correct = false
      }
    }
  }
}

子元件 Template

綁定 button 與子元件字型的 changeColor method。

<template>
    <div class="button" :style="style" v-on:click="changeColor(color)">{{ color }}</div>
</template>

子元件 Script

藉由 $emiit 的方式呼叫父元件的 updatemsg

export default {
  emits: ['update'],
  methods: {
    changeColor(color) {
        this.changeBackground = !this.changeBackground
        this.$emit('update', color)
    }
  }
}

在第二個專案則是改採 Big Components 的方式進行設計,省略了 Props、Events 的設計,當然這會使得檔案過於龐大,日後的擴充勢必要在拆分檔案內容。

使用 Router 組織頁面

Hash mode vs Html5 mode

原先是使用 Html5 mode 發現藉由 Router 導向的頁面如果 Refresh Page 就會在 GitHub Page 出現 HttpStatus 404。經查後才發現必須要搭配網頁伺服器的 Redirect 至 public/index.html 才不會出現這個問題。然而對於 GitHub Page 沒有辦法進行網頁伺服器的調整,因此最後改以 Hash mode 的方式,缺點是 URL 會出現 /#/ ,且也不利於 SEO。然而這點對於本次專案 Vue App 的情境而言不需要 SEO,所以部會造成任何問題。

瀏覽器就可以直接連結 Vue Router 導向的葉面,例如 色彩的沉浸模式 | https://color.sdwh.dev/#/full/teal

Vue Router - Different History modes

專案發布

⭐ 專案發布的詳細步驟說明 | sdwh.dev

成果

🎮 灰色と青 | 🐱‍💻 灰色と青 | GitHub Repo

🎮 す早く | Mental Excersice | 🐱‍💻 す早く | GitHub Repo

小結

使用 Vue CLI 並使用 Visual Studio Code 進行開發,享受流暢的開發體驗,應用前端框架為提供了開發複雜應用程式的可能性。

精進方向結合 Firebase 資料保存、讀取的功能,探索 Vuex 儲存狀態,讓元件之間可以共用資料,探索 axios,呼叫 API 載入資料 💎

參考資料

重新認識 Vue.js

重新認識 Vue.js:008天絕對看不完的 Vue.js 3 指南