【Vue】Vue項目實戰(zhàn)1

此文項目代碼:https://github.com/bei-yang/I-want-to-be-an-architect
碼字不易踩验,辛苦點個star,感謝转唉!

引言


此篇文章主要涉及以下內(nèi)容:

  1. UI庫選型思路
  2. 全家桶融會貫通vue-router+vuex
  3. 前端登錄和權(quán)限控制
  4. 前后端交互
  5. 解決跨域問題

學習資源


  1. UI庫:cube-ui
  2. 后端接口編寫:koa
  3. 請求后端接口:axios
    著重關(guān)注請求暑劝、響應(yīng)攔截
  4. 令牌機制:Bearer Token
  5. 代理配置、mock數(shù)據(jù):vue-cli配置指南厢塘、webpack配置指南

開發(fā)環(huán)境


  1. vscode下載
  2. node.js下載

選擇一個合適的UI庫


vue add cube-ui

擴展性


任何ui庫都不能滿足全部的業(yè)務(wù)開發(fā)需求,都需要自己進行定制和擴展,組件化設(shè)計思路至關(guān)重要

登錄頁面


  • 安裝router:vue add router
  • 安裝vuex:vue add vuex
  • 配置路由router.js
import Vue from "vue";
import Router from "vue-router";
import Home from "./views/Home.vue";
import Login from "./views/Login.vue";

Vue.use(Router);

const router = new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: [
    {
      path: "/",
      name: "home",
      component: Home
    },
    {
      path: "/login",
      name: "login",
      component: Login
    },
    {
      path: "/about",
      name: "about",
      meta: {
        auth: true
      },
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () =>
        import(/* webpackChunkName: "about" */ "./views/About.vue")
    }
  ]
});

// 路由守衛(wèi)
router.beforeEach((to, from, next) => {
  if (to.meta.auth) {
    // 需要登錄
    const token = localStorage.getItem("token");
    if (token) {
      next();
    } else {
      next({
        path: "/login",
        query: { redirect: to.path }
      });
    }
  } else { // 不需要登錄驗證
    next()
  }
});

export default router;

  • 登錄狀態(tài)闺属,store.js
export defalut new Vuex.Store({
  state: {
    isLogin: false
  },
  mutations: {
    setLoginState(state, b) {
      state.isLogin = b;
    }
  },
  actions: {}
})
<div>
    <div class="logo">
      <img src="https://img.kaikeba.com/logo-new.png"
           alt>
    </div>
    <!-- <cube-button>登錄</cube-button> -->
    <cube-form :model="model"
               :schema="schema"
               @submit="handleLogin"
               @validate="haneldValidate"></cube-form>
  </div>

分別設(shè)置modelschema

 model: {
        username: "",
        passwd: ""
      },
      schema: {
        // 表單結(jié)構(gòu)定義
        fields: [
          // 字段數(shù)組
          {
            type: "input",
            modelKey: "username",
            label: "用戶名",
            props: {
              placeholder: "請輸入用戶名"
            },
            rules: {
              // 校驗規(guī)則
              required: true
            },
            trigger: "blur"
          },
          {
            type: "input",
            modelKey: "passwd",
            label: "密碼",
            props: {
              type: "password",
              placeholder: "請輸入密碼",
              eye: {
                open: true
              }
            },
            rules: {
              required: true
            },
            trigger: "blur"
          },
          {
            type: "submit",
            label: "登錄"
          }
        ]
      }

model就是輸入框綁定的數(shù)據(jù),schema是具體的表單的描述周霉,會動態(tài)渲染一個表單
每次校驗都會觸發(fā) handleValidate方法掂器,打印校驗的結(jié)果

handleValidate(ret){
  console.log(ret)
}

點擊登錄觸發(fā)handleLogin發(fā)起登錄請求
Login.vue

handleLogin (e) {
      // 組織表單默認提交行為
      e.preventDefault();
      // 登錄請求
      //   this.login(this.model) // 使用mapActions
      this.$store
        .dispatch("login", this.model)
        .then(code => {
          if (code) {
            // 登錄成功重定向
            const path = this.$route.query.redirect || "/";
            this.$router.push(path);
          }
        })
        .catch(error => {
          // 有錯誤發(fā)生或者登錄失敗
          const toast = this.$createToast({
            time: 2000,
            txt: error.message || error.response.data.message || "登錄失敗",
            type: "error"
          });
          toast.show();
        });
    },

登錄動作編寫:提交登錄請求,成功后緩存token并且提交至store
store.js

import Vue from "vue";
import Vuex from "vuex";
import us from "./service/user";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    isLogin: localStorage.getItem('token') ? true : false
  },
  mutations: {
    setLoginState(state, b) {
      state.isLogin = b;
    }
  },
  actions: {
    login({ commit }, user) {
      // 登錄請求
      return us.login(user).then(res => {
        const { code, token } = res.data;
        if (code) {
          // 登錄成功
          commit("setLoginState", true);
          localStorage.setItem("token", token);
        }
        return code;
      });
    }
  }
});

編寫接口服務(wù)
service/user.js

import axios from 'axios'

export default {
    login(user){
        return axios.get('/api/login', {params:user})
    }
}

webpack devServerpost支持不好俱箱,這里暫時使用get請求

configureWebpack: {
  devServer: {
    before(app) {
      app.get('/api/login', function (req, res) {
        const {
          username,
          passwd
        } = req.query;
        console.log(username, passwd);

        if (username === 'xxx' && password === 'xxx') {
          res.json({
            code: 1,
            token: 'abcdtoken'
          });
        } else {
          res.status(401).json({
            code: 0,
            message: '用戶名或密碼錯誤'
          })
        }
      })
    }
  }
}

檢查點


  • 如何路由守衛(wèi)
  • 如何進行異步操作
  • 如何保存登錄狀態(tài)
  • 如何模擬接口

http攔截器


有了token之后国瓮,每次http請求發(fā)出,都要加在header

// interceptor.js
const axios=require('axios')

export default function(){
  axios.interceptors.request.use(config=>{
    const token=localStorage.getItem('token')
    if(token){
      config.headers.token=token;
    }
    return config;
  })
}

// 啟用 main.js
import interceptor from './interceptor'
interceptor()

可以通過登錄接口測試攔截器效果狞谱,但是不合理乃摹,我們編一個用戶信息接口,需要攜帶token才能訪問

// mock接口 vue.config.js
function auth(req, res, next) {
  if (req.headers.token) {
    // 已認證
    next()
  } else {
    res.sendStatus(401)
  }
}

app.get('/api/userinfo', auth, function (req, res) {
  res.json({
    code: 1,
    data: {
      name: 'xxx',
      age: '18'
    }
  })
})
// 清localStorage測試

注銷


  • 需要清除token緩存的兩種情況:
    1. 用戶主動注銷
    2. token過期
  • 需要做的事情:
    1. 清空緩存
    2. 重置登錄狀態(tài)
  • 用戶主動注銷
// app.vue
<button v-if="$store.state.isLogin" @click="logout">注銷</button>
export default {
  methods: {
    logout() {
      this.$store.dispatch('logout')
    }
  },
}
// store.js
    logout({ commit }){
      // 清緩存
      localStorage.removeItem('token')
      // 重置狀態(tài)
      commit("setLoginState", false);
    }
  • token過期導致請求失敗的情況可能出現(xiàn)在項目的任何地方跟衅,可以通過響應(yīng)攔截統(tǒng)一處理

http攔截響應(yīng)


統(tǒng)一處理401狀態(tài)碼孵睬,清理token跳轉(zhuǎn)login

// interceptor.js
export default function(vm){ // 傳入vue實例
  // ...
  // 響應(yīng)攔截
  // 這里只關(guān)心失敗響應(yīng)
  axios.interceptors.response.use(null,err=>{
    if(err.respinse.status===401){
      // 清空vuex和localstorage
      vm.$store.dispatch('logout')
      // 跳轉(zhuǎn)login
      vm.$router.push('/login')
    }
    return Promise.reject(err)
  })
}

// app.vue
const app=new Vue({...}).$mount('#app')
interceptor(app) // 傳入vue實例

深入理解令牌機制


  • Bearer Token規(guī)范
    • 概念:描述在HTTP訪問OAuth2保護資源時如何使用令牌的規(guī)范
    • 特點:令牌就是身份證明,無需證明令牌的所有權(quán)
    • 具體規(guī)定:在請求頭中定義Authorization
    Authorization:Bearer <token>
    
  • Json Web Token規(guī)范
    • 概念:令牌的具體定義方式
    • 規(guī)定:令牌由三部分構(gòu)成‘頭伶跷,載荷掰读,簽名’
    • 頭:包含加密算法、令牌類型等信息
    • 載荷:包含用戶信息叭莫、簽發(fā)時間和過期時間等信息
    • 簽名:根據(jù)頭蹈集、載荷及秘鑰加密得到的哈希串 HMac Sha1 256
  • 實踐:
    • 服務(wù)端:-server/server.js
    const Koa = require("koa");
    const Router = require("koa-router");
    // 生成令牌、驗證令牌
    const jwt = require("jsonwebtoken");
    const jwtAuth = require("koa-jwt");
    
    // 生成數(shù)字簽名的秘鑰
    const secret = "it's a secret";
    
    const app = new Koa();
    const router = new Router();
    
    router.get("/api/login", async ctx => {
      const { username, passwd } = ctx.query;
      console.log(username, passwd);
    
      if (username == "kaikeba" && passwd == "123") {
        // 生成令牌
        const token = jwt.sign(
          {
            data: { name: "kaikeba" }, // 用戶信息數(shù)據(jù)
            exp: Math.floor(Date.now() / 1000) + 60 * 60 // 過期時間
          },
          secret
        );
        ctx.body = { code: 1, token };
      } else {
        ctx.status = 401;
        ctx.body = { code: 0, message: "用戶名或者密碼錯誤" };
      }
    });
    
    router.get(
      "/api/userinfo",
      jwtAuth({ secret }),
      async ctx => {
        ctx.body = { code: 1, data: { name: "jerry", age: 20 } };
      }
    );
    app.use(router.routes());
    app.listen(3000);
    
  • 修改配置文件:啟用開發(fā)服務(wù)器代理雇初,vue.config.js
    devServer: {
      proxy: {
        "/api": {
            target: "http://127.0.0.1:3000/", 
            changOrigin: true
        }
      }, 
    
  • 攔截器的修改拢肆,interceptor.js
    config.headers.Authorization='Bearer'+token
    

你的贊是我前進的動力

求贊,求評論抵皱,求分享...

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末善榛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子呻畸,更是在濱河造成了極大的恐慌移盆,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伤为,死亡現(xiàn)場離奇詭異咒循,居然都是意外死亡据途,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門叙甸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颖医,“玉大人,你說我怎么就攤上這事裆蒸∪巯簦” “怎么了?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵僚祷,是天一觀的道長佛致。 經(jīng)常有香客問我,道長辙谜,這世上最難降的妖魔是什么俺榆? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮装哆,結(jié)果婚禮上罐脊,老公的妹妹穿的比我還像新娘。我一直安慰自己蜕琴,他們只是感情好萍桌,可當我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奸绷,像睡著了一般梗夸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上号醉,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天反症,我揣著相機與錄音,去河邊找鬼畔派。 笑死铅碍,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的线椰。 我是一名探鬼主播胞谈,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼憨愉!你這毒婦竟也來了烦绳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤配紫,失蹤者是張志新(化名)和其女友劉穎径密,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躺孝,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡享扔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年底桂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惧眠。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡籽懦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出氛魁,到底是詐尸還是另有隱情暮顺,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布呆盖,位于F島的核電站拖云,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏应又。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一乏苦、第九天 我趴在偏房一處隱蔽的房頂上張望株扛。 院中可真熱鬧,春花似錦汇荐、人聲如沸洞就。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旬蟋。三九已至,卻和暖如春革娄,著一層夾襖步出監(jiān)牢的瞬間倾贰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工拦惋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留匆浙,地道東北人。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓厕妖,卻偏偏與公主長得像首尼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子言秸,可洞房花燭夜當晚...
    茶點故事閱讀 43,587評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 簡介 參考博客: 全棧開發(fā)實戰(zhàn):用Vue2+Koa1開發(fā)完整的前后端項目(更新Koa2)前置技能: 具備Vue和K...
    Ghamster閱讀 8,152評論 1 15
  • koa2+vue 用vue-cli搭建前端項目 用koa2搭建后臺软能,給前端提供數(shù)據(jù)訪問接口 項目結(jié)構(gòu) 用vue-c...
    我才是seven閱讀 9,837評論 2 13
  • 我一直覺得,爬蟲是許多web開發(fā)人員難以回避的點举畸。我們也應(yīng)該或多或少的去接觸這方面查排,因為可以從爬蟲中學習到web開...
    梔子花_ef39閱讀 777評論 0 2
  • 文章目錄 nodejs基礎(chǔ) express koahello worldkoa路由nodemon 監(jiān)控文件修改ko...
    好名字都讓你們用了閱讀 1,018評論 2 2
  • 昨天看到說堅持練習的人懶癌會慢慢被治愈,隨著每天不間斷的練習俱恶,早起雹嗦,當我開始每天比別人多一個清晨的時間去努力練習阿...
    心元愚舍麗云閱讀 967評論 3 10