---
# System prepended metadata

title: 前端導入 Vue 建議事項
tags: [RWD, SEO, Vuex, Vue, SPA]

---

# 前端導入 Vue 建議事項
###### tags: `Vue` `SPA` `SEO` `RWD` `Vuex` 

## 吃完 Vue 全家餐，實現所有願望
Vue.js 在處理前段架構解決方案時，仿間有一套俗稱「全家餐」的套件，套件組成如下：
* vue
* vuex(狀態管理)
* vue-router(路由)
* axios(http請求工具)
* vue-cli(建構工具)


另外建議可以使用的插件
* VeeValidate (表單驗證)
* Yup (檢核庫)
* Nuxt.js (處理SEO的靜態渲染)
* Webpack (打包程式)
* Bootstrap (模板)
* bootstrap-vue (vue專用的bootstrap不須載入jquery)

從瀏覽器發出 Request 處理，到前端處理邏輯驗證，一路到後端 API 存取，一個完整的解決方案，十分推薦!

## 建議導入 SPA
如果網站服務很要求使用者體驗，希望操作起來更順暢，同時能做多工，這時就很推薦導入 SPA，
但導入後，由於 SPA 只有單一頁面，初次載入即下載全部內容，導致首次下載速度變慢，外加 SPA 裡面只有放程式邏輯，會讓 SEO 搜尋不到內容，導致網站排名下滑，故此建議下列解決辦法：
* 加快首次下載速度
透過 lazyLoad 的設計，把程式邏輯跟元件延後下載，減輕首次載入負擔

* 不支援 SEO
建議透過 靜態化 套件，將網頁產生靜態頁面，讓 SEO 可搜尋到內容，詳細做法可參閱[連結](#導入-SPA-架構後，解決-SEO-的建議做法)

如何導入 SPA 實際作法如下：
* 用vue-cli建立專案
vue-cli 為官方提供的開發工具，用於快速開發大型 SPA 網站，省去瑣碎的工作，快速建立起開發環境

* 建立專案時選用vue-router
Vue Router 就是由前端模擬的路由，跟過去不同的是，換頁通常會由後端處理。而 Vue Router 存在就是為了實現 SPA（單頁式應用），當我們切換畫面時候就不需要向後端發出請求。


## 建議導入狀態管理 Vuex
Vuex採用單向數據流的概念，避免系統龐大下數據流混亂的情況，讓資料狀態是清晰可以預測的，便於維護與擴充；當元件遇到多個元件共享狀態，單向數據流的簡潔性很容易被破壞，因此 vuex 將元件的共享狀態抽取出來，以一個**全域單例模式管理**

**Vuex的單向數據流**
通過元件執行 Actions , Actions 執行 Mutations , Mutations 更新 State 的狀態，更新後會觸發頁面資料更新，所有的狀態集中管理，所有的元件都能通過這個入口改變 state 中的資料

![](https://i.imgur.com/BQV5QkP.png)

在 單向數據流中，Vuex 能為我們帶來幾點好處：
* 直觀的運用狀態
* 結構清晰
* 防止重新載入元件時重複發送http請求
* 不用擔心元件結構出現多層時導致的數據流混亂


**Ｑ：為何透過 Actions 來更新 Mutations 而不是直接更改 Mutations ?**
Ａ：因為 Mutation 只支持同步操作，不支持異步操作，因此所有的異步操作(ex: ajax 請求, setTimeout, Promise 等) 都要放在 Action 中執行。

**Ｑ：為什麼 Mutation 不支持異步操作呢？**
Ａ：因為在 devtools 中會紀錄 Mutations 執行時傳進的 payload 以及改變的 state ，當在 Mutations 進行異步操作時，devtools 不知道 State 什麼時候被改變了，會出現資料不同步並且難以追蹤的狀況。

**Ｑ：如果不按照上面兩點來執行，有什麼防範措施？**
Ａ：添加 strict: true 使 Vuex 存儲進入嚴格模式，在嚴格模式下，任何變異處理函數以外修改 Vuex state 都會拋出錯誤。
```
import txtModules form ./product;
export default new Vuex.Store({
  strict: true,
  modules: {}
})
```


## 導入 SPA 架構後，解決 SEO 的建議做法
SPA 會由單一頁面入口，透過 router 決定渲染的內容，在未做優化的情況下爬蟲只會爬到入口頁面的資料，這樣搜尋引擎優化的效果會不盡理想，如果要提高搜尋引擎內的能見度，需要另外將網頁的內容做處理，有以下幾種做法

解決做法如下：
* 靜態化 (最推薦)
優點
    - 純靜態文件，訪問速度快
    - 對比 SSR，不涉及到服務器負載方面問題
    - 靜態網頁不易遭到駭客攻擊，安全性更高
    
    缺點：
    - 如果動態路由參數多的話不適用
* SSR 服務器渲染
    * 開發條件所限，瀏覽器特定的代碼，只能在某些 lifecycle hook 中使用，一些 external library 可能需要特殊處理，才能在 Server 渲染應用程序中運行
    * 環境和部屬要求更高，需要 Node.js server 運行環境
    * 高流量的環境下，請準備對應的服務器負載，並明智地採用緩存
* 預渲染 prerender-spa-plugin
優點
    - 改動小，加個插件就完成
    
    缺點
    - 無法使用動態路由
    - 只適用少量頁面的項目，頁面多達幾百個的情況下，打包會很慢
* 使用 Phantomjs 對爬蟲做處理
優點
    - 完全不用改動項目代碼，按原本的 SPA 開發即可，對比開發 SSR 成本小的多
    - 對已用 SPA 開發完成的項目，這是不二之選
    
    缺點
    - 部屬需要 node 服務器支持
    - 爬蟲訪問比網頁訪問要慢一些，因為定時要定時資源加載完成才返回給爬蟲

在不使用 node.js server 的情況下
可以使用靜態化或預渲染

## UI/UX 套件建議使用 Bootstrap
前端頁面開發時，可以使用 UI/UX 套件減少開發的時間，建議可以使用通用性比較高的bootstrap , bootstrap 有兩大優點：
* Grid System
    * bootstrap 有預設不同尺寸的樣板，在開發上可以針對需求直接使用預設的標籤
    * 在設計響應式網頁時，可以依照不同螢幕寬度使用不同的標籤，使排版更精簡

* UI
    * 避免重工，可以應用一些現成的功能
    * 較複雜的響應式設計則很快就可以應用
    * 在不同專案中、或是同專案不同開發者
    * 可以在開發過程中取得應用及解讀程式碼的一致性
    * Bootstrap 也提供了一些速成樣板
    * 確保跨瀏覽器的閱讀相容性

Bootstrap 是以 jquery 為基底使用的
如果想要使用 bootstrap 又不想加載 jqeury
可以使用 bootstrap-vue

## 建議導入 Validation schema
前端表單把資料傳到後端前，會檢查資料的正確性，處理這部分的流程建議使用 Validation schema。

傳統的表單驗證，無法處理多欄位連動檢核，外加檢核條件跟表單綁在一起，若在不同表單中有相同驗證邏輯，必須將相同邏輯複製過去，外加日後需要變更時，需要更改不同地方，容易造成遺漏，處理起來很麻煩。

透過 Validation schema + yup + vuex 不僅能做到多欄位連動的檢核，透過 yup 檢核庫自訂的檢核邏輯，同時可檢核多個相同邏輯的資料，減少程式出錯率。

建議做法
* Yup (檢核庫)
     yup 是針對數值驗證的檢核庫，針對客戶端的資料做驗證，yup 將驗證功能切分成單獨的部分，每個部份可以單獨使用或者合併使用，透過合併使用的方法去做到多欄位連動。

    詳細可參考：[連結](https://github.com/jquense/yup)

* VeeValidate (表單驗證插件)
    vee-validate 可以很輕鬆地處理複雜的驗證邏輯，並支持同步、異步驗證，並支援 yup 的 Validation schema 驗證，可輕鬆完成表單驗證。

    詳細可參考：[連結](https://vee-validate.logaretm.com/v4/guide/validation)

## 建議導入打包工具 Webpack
現今的很多網頁其實可以看做是功能豐富的應用，它們擁有著複雜的 Javascript 程式碼和一大堆依賴包。為了簡化開發的複雜度，可以使用 webpack 之類的打包工具


建議原因：
* 兼容 CommonJS & AMD & ES6 模組規範
* Bundle 效率高
* 可編譯語法和打包資源 (css, img, font…)入 JS 內
* JS 程式碼封裝
* 網路資源豐富、社群強大，Plugin 多

vue cli 提供六種基本樣板如下：
- webpack
- webpack-simple
- browserify
- browserify-simple
- pwa
- simple

多使用 webpack 於新增專案時選擇 webpack 即可

## 撰寫 Vue 語法上幾個 Tips
俗話說的好，拿別人的錯誤的經驗，就能減少走錯路的機會，程式開發時也是如此，撰寫 Vue 時，有幾條應該要遵守的慣例，描述如下：

注意事項：
1. **在使用 v-for 時，永遠要綁定 key 值**
如果是透過 vue-cli 打造的專案，使用 v-for 卻沒有綁定 key 時，系統會報錯提醒，例如：
    ```
    <div v-for="item in 5"> {{item}}</div>
    ```
    直到把程式碼修改完成，才會停止錯誤。
    ```
    <div v-for="item in 5" :key="item.id"> {{item}}</div>
    ```
    為何會發生這個問題呢？想像一下，當我們不綁定 key 時，如果產生了十個 li 節點，要怎麼只控制其中某個節點，綁定 key 用意，在於 v-for 產生的組件都是**唯一的**，以利日後開發。

1. **維持固定的寫法**
使用 v-on & v-bind 或是 slot 時，常常會搭配對應的縮寫
    * '@' -> v-on
    * ':' -> v-bind
    * '#' -> slot 
    
    這是相當方便、且可讀性高的寫法，但有時候在同一個專案同時出現 v-on:click&@click 的狀況，這就相當惱人，跟 Code Style 依樣，要馬就全部用縮寫，不然就不要使用。

1. **避免 v-for & v-if 使用在同一個組件上**
使用 v-for 時，你想加入一些條件判斷讓他只顯示部份內容，可能程式會這樣撰寫。
    ```
    <h1>Admin Users</h1>
    <div v-for='user in users' v-if='uesr.isAdmin'>
    ```
    但實際運行時，會發覺有些問題，主因是 v-for 優先度高於 v-if，也就是會先迭代所有元素後，最終才檢查 v-if 的部分，如果有類似需求，建議使用 computed 屬性來達到效果。
    ```
    <div v-for="user in adminUsers" :key="user.key"> {{user.name}} </div>
    
    computed: {
        adminUsers() {
            return this.users.filter(user=> user.isAdmin)
        }
    }
    ```


1. **永遠檢查傳入的 props**
父子層溝通時很時常透過 props 屬性，為求方便承接參數時，並不會額外做檢查，就可能寫出這段程式。
    ```
    props: ['currentUser', 'products']
    ```
    
    但實際上不應該完全相信傳進來的資料，就跟後端會再重新驗證前端傳來的資料是一樣概念，適當的檢查可避免很多錯誤，上述範例建議可改寫為以下
    ```
    props: {
        currentUser: {
            type: Object,
            default: null,
            required: true,
        },
        products: {
            type: Object,
            default: [],
            required: true,
        },
    }
    ```
    

1. **不要在 template 中塞入太複雜的邏輯**
很多時候我們使用 view 操作時，在元素上常常會加入一些基本 js 邏輯，例如：
    ```
    <h1> {{user.name}} </h1>
    ```
    但請避免在 template 中塞入太多複雜得 js 邏輯，像是：
    ```
    <h1> {{user.name.split(' ').reverse().join('')}} </h1>
    ```
    如同上面 v-for & v-if 情況依樣，最好使用 computed 來處理
    ```
    <h1> {{reversedName}} </h1>
    computed: {
        reversedName() {
            return this.user.name.split(' ').reverse().join('')
        }
    }
    ```


1. **避免直接去操作DOM元素**
vue 會透過 virtual DOM 的概念去處理資料變動時的畫面刷新，不僅可更有效率的處理刷新，也可節省許多資源，因此我們應該避免直接操作 DOM，如過真的要選取，建議使用 ref 屬性：
    ```
    <h1 ref="title"> Hello World </h1>
    
    this.$refs.title // 利用這樣去選取該元素並作需要的操作
    ```

## 後端 API 串接時，建議幾種套件
vue 取得後端資料有以下方法
* vue-resource(原官方函式庫)
* Axios
* jQuery ($.ajax, $.get, etc.)
* Fetch API

Vue 原作者推薦使用 axios 替代原本官方的 vue-resource,
現在主要都使用 axios , axios 對 nodejs 的支援度較高
有以下特點
1. 在瀏覽器中發送 XMLHttpRequests 請求
1. 在 node.js 中發送 http請求
1. 支持 Promise API
1. 攔截請求和響應
1. 轉換請求和響應數據
1. 取消請求
1. 自動轉換 JSON 數據
1. 客户端支持保護安全免受 CSRF/XSRF 攻擊

## 使用 Router 的注意事項
* 如何防止使用者未登入就拜訪頁面:
在 routers 內需要驗證的項目上加上屬性 
meta: { requiresAuth: true}
* 頁面只異動部分區塊的話可以用嵌套路由，加上 children 屬性
* Component 建議使用 Lazy Loading 的方式加載
例:component: ()=>import("@/view/pageB")
