iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 22
1

已經過了 21 天,終於要進入 Vuex 相關的章節。

在開始寫 code 之前,先分享一個 Vue 核心成員 Chris Fritz 今年的演講影片,
介紹了 Vue 的七個少為人知的小技巧,看完真的收穫不少。
而今天會用到這個演講介紹的一個小技巧,幫助專案的 Vuex module 綁定(Module Registration)
影片連結: 7 Secret Patterns Vue Consultants Don’t Want You to Know

今天用到的是影片中介紹的第三點(從12:49)開始。

懶得看影片就讓我來簡單介紹一下怎麼輕鬆優化使用 Vuex Module

Module Registration

首先這個小技巧不限於在 Nuxt 應用中使用。

讓我們先建立好綁定 Vuex 所需的目錄結構:

022-001

上面可以看到建立了三個檔案:

  • modules
    • auth.js: 幫我們處理登入功能的 Module
    • index.js: 協助綁定 Module 的程式碼
  • index.js: Vuex instance

modules/index.js

import camelCase from 'lodash/camelCase'

const requireModule = require.context('.', false, /\.js$/)
const modules = {}

requireModule.keys().forEach(fileName => {
    // Don't register this file as a Vuex module
    if (fileName === './index.js') return

    const moduleName = camelCase(
        fileName.replace(/(\.\/|\.js)/g, '')
    )
    modules[moduleName] = {
        namespaced: true,
        ...requireModule(fileName)
    }
})

export default modules

第一行可以看到我們引用了 lodash 的 camelCase之前的篇章已經有引入 lodash。

而核心的部分主要是使用 require.context 引入所有 ./modules 目錄底下除了 index.js 的 Vuex modules,
並且利用 requireModule.keys().forEach 主動為所有 module 加上 namespaced: true 的屬性。

store/index.js

經過了上面 modules/index.js 的綁定後,
store/index.js 就可以寫得像下面這樣簡潔:

import Vuex from 'vuex'
import modules from '@/store/modules'

const createStore = () => {
  return new Vuex.Store({
    modules
  })
}

export default createStore

有了 Module Registration 之後,
就不必再為了新增 module 而修改 store/index.js。

modules/auth.js

介紹完 Module Registration 後,
接著開發使用 firebase 的服務 authentication 來串接登入功能。

用 firebase 登入非常容易,這邊將功能拆分為三個 actions:

  • signInWithEmail: 用 Email 登入
  • signOut: 登出
  • signInAuto: 自動登入

請看程式碼:

import { auth } from '@/services/fireinit.js'

export const state = {
  user: null
}
export const mutations = {
  setUser (state, payload) { state.user = payload }
}
export const actions = {
  signInWithEmail ({ commit }, payload) {
    return auth.signInWithEmailAndPassword(payload.email, payload.password)
      .then(user => user)
  },
  signOut ({ commit }) {
    return auth.signOut()
      .then(() => {
        commit('setUser', null)
      })
  },
  signInAuto ({ commit }) {
    return new Promise((resolve, reject) => {
      auth.onAuthStateChanged(user => {
        if (user) {
          commit('setUser', user)
        }
        resolve(user)
      })
    })
  }
}

上面程式碼可以看到,
第一行引入了 fireinit.js

import { auth } from '@/services/fireinit.js'

還記得這是上一篇所完成的套件對吧?
fireinit 只是簡單的將 firebase 的 auth 做簡單的包裝。

用到的三個 firebase auth service

auth.signInWithEmailAndPassword

auth.signOut

auth.onAuthStateChanged

應該都很直覺就不多解釋了。

登入頁面切版 & 登入驗證

記得已經在第 12 天 已經建立過所有需要的頁面,但是還沒有切版呢

已經有了 firebase authentication 的功能,
就差一個介面來登入。

pages/admin/signIn/index.vue

先看一下 template 的部分:

這邊就不提供申請帳號的功能,因為畢竟只有作者自己能編輯。
登入的驗證一樣是透過前天介紹的 simple-vue-validator。
這邊的切版簡單使用了 Vuetify 的 v-text-field,
完全不必再寫任何一行 CSS。

022-002

javascript 登入和驗證邏輯

解釋:
首先引入 SimpleVueValidation 並使用 conservative 模式,
這個模式是被動的驗證,必須等到使用者按下登入按鈕後,才會真正第一次執行驗證,
並在第一次驗證後模式自動切換為即時驗證。
另外 data 中的 error 用來顯示非同步的登入錯誤訊息,
而這邊可能錯誤原因為帳號密碼輸入錯誤。

import Vue from 'vue'
import SimpleVueValidation from 'simple-vue-validator'
const Validator = SimpleVueValidation.Validator

Vue.use(SimpleVueValidation, { mode: 'conservative' })

export default {
  layout: 'admin',
  data: vm => ({
    email: '',
    password: '',
    error: false,
  }),
  validators: {
    email: value => Validator.value(value).required().email(),
    password: value => Validator.value(value).required()
  },
  methods: {
    signIn () {
      this.$validate()
        .then(success => {
          if (success) {
            this.$store.dispatch('auth/signInWithEmail', {
              email: this.email,
              password: this.password
            })
              .then(data => {
                if (data.status === 'success') {
                  this.error = false
                  this.$router.push('/admin')
                } else {
                  this.email = ''
                  this.password = ''
                  this.error = true
                  this.$refs.email.focus()
                  this.validation.reset()
                }
              })
          }
        })
    }
  }
}

layouts/admin.vue

之前在第 11 篇已經將 layouts admin 切版完,
現在只需要新增自動登入的功能,以及將未登入的狀態導頁至登入頁。

export default {
  beforeCreate () {
    this.$store.dispatch('auth/signInAuto')
      .then(user => {
        user 
          ? this.$router.push('/admin') 
          : this.$router.push('/admin/signIn')
      })
  },
  methods: {
    signOut () {
      this.$store.dispatch('auth/signOut')
        .then(() => {
          this.$router.push('/admin/signIn')
        })
    }
  }
}

這邊只需在 beforeCreate 執行 action(auth/signInAuto),
並判斷要導頁至哪裡。
登出功能更只要執行 action(auth/signOut),並導頁。

登入 Demo

用了 Vuetify 後,不只是切版變得容易,
用了 simple-vue-validator 後,更是可以輕鬆快速搞定表單驗證。

登入畫面:

022-003

輸入密碼:

022-004

simple-vue-validator 驗證:

022-005

非同步驗證 Email 或密碼錯誤:

022-006

登入成功:

自動導頁到 admin 首頁,
並且可以看到右下 module/auth 的 state.user 已成功從 null 變成 object。

022-007

程式碼放在以下 branch:

022-sign-in-out-auto

指令:

git clone -b 022-sign-in-out-auto --single-branch https://github.com/hunterliu1003/blog.git
cd blog
npm install
npm run dev
# localhost:8787/admin/signIn

上一篇
#21 套件安裝及設定: Firebase 基礎設定
下一篇
#23 Vuex module: tags, post
系列文
用 Nuxt.js 2.0, Vuetify, Storybook, Firebase 建一個 Blog30

尚未有邦友留言

立即登入留言