測試平臺開發(二) 高逼格登入頁面

測試老樹發表於2020-11-02
測試平臺開發(二) 高逼格登入頁面

怎麼樣?哎喲,不錯哦。本文就帶大家一起用 Vue + Element-UI 把這個不錯的登入頁面開發出來。

專案結構分析

在使用 Vue-CLI 建立 2.x 的腳手架專案後,會生成如下目錄檔案:

測試平臺開發(二) 高逼格登入頁面

針對這個目錄檔案我寫了一個腦圖進行說明:

(文字稍微有點多,趕時間的同學看紅色部分就可以了

測試平臺開發(二) 高逼格登入頁面

紅色最多的是 src 資料夾,寫的程式碼大部分都是放在這個資料夾下面。

重要提示,一下就刷到這裡,只看文字不看圖的同學,還是把圖多看幾秒哦,看不清楚,可以放大看,哈哈哈。

程式執行流程

按照我自己對 Vue 的理解,畫了一張幾個主要檔案之間程式呼叫執行的流程圖:

(水平有限,有錯誤請指正)

測試平臺開發(二) 高逼格登入頁面

圖中簡單描繪了 index.html、main.js、App.vue、store\index.js、router\index.js、views\login\index.vue 這幾個檔案之間的呼叫邏輯。暫時沒有用到 components,因為登入介面不涉及到功能元件,只是個頁面,程式碼放在 views 資料夾下即可。

哈哈我又來提醒了,只看文字不看圖的同學,多看幾眼,看不清楚,請放大!

原始碼

本文接下來會對這些檔案逐個進行程式碼解析,為了不讓文章變得冗長,只貼部分程式碼,完整程式碼請到 GitHub 獲取:

https://github.com/dongfanger/sprint-frontend

如果想把這個專案復原出來,只看我寫的文章是不夠的,git clone順手點個 star,學習效果更好哦。

index.html

index.html 是專案中唯一的 html 檔案

因為 Vue 實現的是單頁面應用。單頁面應用,簡單理解就是隻有一個頁面,其他頁面都是通過元件的形式掛載到這個頁面上的,這樣頁面切換就會更快速,如桌面應用一般絲滑順暢。

其他頁面的掛載點其實就是一個 div,其他頁面都在放在這個 div 裡面的:

    <div id="app"></div>

views\login\index.vue

本專案是基於 Element-UI 的,需要使用 npm 安裝一下

npm i element-ui -S

.vue 檔案是 Vue 框架的程式碼檔案,分為3個部分

<template>
    html 模板
</template>

<script>
	javascript 指令碼
</script>

<style lang="scss" scoped>
	css 樣式
</style>

為了文章簡潔,本文不展示 css 樣式的程式碼。

template html 模板程式碼如下,實現了使用者名稱、密碼、登入等輸入框和按鈕:

<template>
  <div class="loginbody" :style="`background-image: url(${appInfo.backgroundImageUrl})`">
    <div class="login-box">
      <div class="login-title">
        <img class="login-logo" :src="appInfo.loginLogoUrl" alt="logo" />
        <p>{{ appInfo.title }}</p>
      </div>

      <div class="login-info">
        <el-form ref="form" class="form-box" :model="form" :rules="formRules">
          <el-form-item :label="'使用者名稱'" prop="username">
            <el-input v-model="form.username" placeholder="請輸入使用者名稱" @keyup.enter.native="login" ref="username-input">
            </el-input>
          </el-form-item>
          <el-form-item :label="'密碼'" prop="password">
            <el-input
                    v-model="form.password"
                    placeholder="請輸入密碼"
                    type="password"
                    show-password
                    @keyup.enter.native="login"
            ></el-input>
          </el-form-item>
          <el-form-item>
            <div class="clear">
              <el-checkbox
                      v-model="form.rememberMe"
                      :value="true"
                      :label="'記住密碼'"
                      name="type"
                      class="remember-checkbox"
              ></el-checkbox>
              <span class="self-right forgetPwd" @click="forgetPwd">忘記密碼?</span>
            </div>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="login" class="login-btn" :loading="isLoging">
              {{ "登入" }}
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>

其中頁面背景圖、logo 圖、頁面標題是通過 Vuex 來存取的。

Vuex 是 Vue 的狀態管理工具。Vue 元件之間資料傳遞一般是通過 export 和 import 的方式,但是對於全域性資料,這種方式很難管理和維護。Vuex 作為中間媒介,幫助元件之間更好的傳遞資料。

javascript 指令碼程式碼如下,給 template 模板填充資料,定義元素行為:

<script>
  import { mapGetters } from "vuex";
  export default {
    data() {
      return {
        form: {
          username: "",
          password: "",
          rememberMe: true
        },
        formRules: {
          username: [
            { required: true, message: "請輸入使用者名稱", trigger: "blur" },
            {
              trigger: "blur",
            },
          ],
          password: [{ required: true, message: "請輸入密碼", trigger: "blur" }],
        },
        isLoging: false,
      };
    },
    created() {
    },
    mounted() {
      let autofocusElement = this.$refs["username-input"];
      if (autofocusElement) {
        autofocusElement.focus();
      }
    },
    methods: {
      login() {
      },
      forgetPwd() {
        this.$alert("請聯絡管理員!", "提示", {
          confirmButtonText: "確定",
          callback: action => {
            this.$message({
              type: "info",
              message: `action: ${action}`,
            });
          },
        });
      },
    },
    computed: {
      ...mapGetters(["appInfo"]),
    },
  };
</script>

其中最後幾行的 appInfo 就是定義的全域性變數,使用 Vuex 來傳遞資料。

store\index.js

appInfo 的實現程式碼放在 store\index.js 檔案中:

import Vue from 'vue'
import Vuex from 'vuex'

const navBarLogoUrl = require("@/assets/image/logo.png");
const loginLogoUrl = require("@/assets/image/logo@2x.png");
const backgroundImageUrl = require("@/assets/image/login-bg.png");
const favicon = require("@/assets/image/favicon.png");

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    appInfo: null,
  },
  getters: {
    appInfo: state => {
      if (state.appInfo) {
        return state.appInfo;
      }
      let localAppInfo = localStorage.getItem("AppInfo");
      if (localAppInfo) {
        return JSON.parse(localAppInfo);
      }
      return {
        dsp: "這是公眾號“測試老樹”開發的測試平臺。",
        navBarLogoUrl,
        loginLogoUrl,
        backgroundImageUrl,
        faviconUrl: favicon,
        title: "測試平臺",
      };
    },
  },
  mutations: {
    commitAppInfo(state, appInfo) {
      state.appInfo = appInfo;
    },
  },
  actions: {
    setAppInfo({ commit }, appInfo) {
      let info = {
        dsp: appInfo.dsp,
        title: appInfo.title || "測試平臺",
        navBarLogoUrl: appInfo.navBarLogoUrl || navBarLogoUrl,
        loginLogoUrl: appInfo.loginLogoUrl || loginLogoUrl,
        backgroundImageUrl: appInfo.backgroundImageUrl || backgroundImageUrl,
        faviconUrl: appInfo.faviconUrl || favicon,
      };

      commit("commitAppInfo", info);
      localStorage.setItem("AppInfo", JSON.stringify(info));
      let { faviconUrl, title } = info;

      let iconElement = document.querySelector("#t-icon");
      iconElement.href = faviconUrl;

      document.title = title;
    },
  },
  modules: {},
});

App.vue

App.vue 是根元件,適合做一些初始化工作。

從“程式執行流程”小節的邏輯圖中可以看到,資料儲存的操作就是在 App.vue 呼叫的,程式碼如下:

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
  import { mapActions } from "vuex";
  export default {
    name: "App",
    data() {
      return {};
    },
    created() {
    },
    methods: {
      ...mapActions(["setAppInfo"]),
    },
  };
</script>

methods 呼叫了 setAppInfo 方法,給 appInfo 賦值。

router\index.js

到這裡,登入頁面的程式碼就已經擼完了。是嗎?是的!

但是還無法訪問,因為還沒有給它配置路由,瀏覽器還不知道怎麼才能跳轉到這個登入頁面。

路由就是訪問路徑,讓瀏覽器知道輸入一個 url 該把哪個頁面展示給你看。在以前,頁面跳轉路由都是放到後端來做的,前端請求後端,後端把渲染好的 html 返回給前端。現在時代不同了,前端直接控制了路由,前後端傳遞的資料變少了,訪問體驗也更佳。比如以前位址列 URL 跳轉可能會白屏,現在不會了。

路由配置程式碼是放在 router\index.js 檔案中的,預設 / 展示 Home 頁面,訪問 /login 展示 login 頁面:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import login from "../views/login"

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    meta: {
      requireAuth: true,
    },
  },
  {
    path: "/login",
    meta: {
      title: "測試平臺登入",
    },
    name: "login",
    component: login,
  },
]

const router = new VueRouter({
  routes
})

但是首頁不能直接就給別人看呀,得先登入!所以需要編寫一個攔截器,必須登入後,才可以訪問首頁,否則跳轉到登入頁面:

router.beforeEach((to, from, next) => {
  if (to.matched.some(auth => auth.meta.requireAuth)) {
    let token = localStorage.getItem("token");
    if (token) {
      next();
    } else {
      next({
        path: "/login",
      });
    }
  } else {
    next();
  }
});

main.js

main.js 是程式執行入口,以上所有程式碼都需要在 main.js 中宣告一下:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import Element from "element-ui"
import "./assets/style/global.scss";

Vue.config.productionTip = false

Vue.use(Element)
new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

vue.config.js

vue.config.js 是 Vue 專案配置檔案。比如 index.html 中,頁面 title 是通過 <title><%= htmlWebpackPlugin.options.title %></title> 來定義的,可以在配置檔案中新增自定義,為 “sprint”:

const path = require("path");

function resolve(dir) {
  return path.join(__dirname, dir);
}
module.exports = {
  publicPath: process.env.NODE_ENV === "development" ? "./" : "/frontend/",
  chainWebpack: config => {
    config.plugin("html").tap(args => {
      args[0].title = "sprint";
      return args;
    });
  },
};

簡要回顧

本文首先展示了登入頁面的效果,接著介紹了 Vue-CLI 初始化之後的專案結構,並對程式執行邏輯進行了分析,梳理出來了主要幾個檔案的呼叫流程,最後分別對各檔案的程式碼進行了分析。

相關文章