vue設計我的博客-登入(起始)

githup webpack配置css

1、webpack引入bootstrap

首先bootstrap是依賴於jquery的,因此須要安裝jquery。
一、在下載好jquery以後,須要引入jquery。
在webpack.base.conf.js模塊中,使用webpack的內置模塊ProvidePlugin,能夠自動加載模塊,而不須要使用import等。html

const webpack = require('webpack')

plugins: [
    new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery"
    })
]
//添加好以後,能夠直接找一個頁面測試
//例在HelloWorld.vue添加,審查元素,body中有沒有添加成功
//若是成功者說明引入成功
$("body").append("<div>1</div>");

二、安裝bootstrap,特別提醒若是你安裝jquery的版本是3.3.1,那麼若是安裝的bootstrap的版本是4以上的那麼2者是不兼容的bootstrap的樣式會出現問題,因此我的建議安裝bootstrap3.3.7的。
安裝:npm install --save bootstrap@3.3.7,固然你也能夠直接到官網裏下載。
引用:若是是用npm方式安裝的,那麼記住這個是在node_modules文件中的。
在main.js頭上中引入:前端

import 'bootstrap/css/bootstrap.min.css'
import 'bootstrap/js/bootstrap.min.js'

這個時候可能會報Popper.js文件找不到的錯誤,由於這裏bootstrap依賴Popper.js文件,用npm install --save Popper.js安裝一下就能夠了。vue

剛剛不是說也能夠直接在官網上下載下來的,若是是直接下載的。將下載包移動到你放置靜態文件的文件夾中解壓出來(我比較推薦這種作法,由於這樣比較好管理),通常不會怎麼變化的靜態文件我會存放在「static」目錄下。node

//在`webpack.base.conf.js文件`中alias模塊
//這裏的resolve是一個方法

function resolve (dir) {
  //join方法用於將多個字符串結合成一個路徑字符串
  //path在node中會常常用到能夠仔細瞭解一下path的各類方法
  //__dirname:獲取當前文件所在目錄的完整絕對路徑
  return path.join(__dirname, '..', dir)
}

alias: {
    'bootstrap':resolve('static/bootstrap'),
}

//main.js
//這裏的bootstrap指代的就是上面的../static/bootstrap
import 'bootstrap/css/bootstrap.min.css'
import 'bootstrap/js/bootstrap.min.js'

到此開發時的基本樣式框架已經準備完畢。jquery


2、登入頁面

最終結果:圖片描述webpack

問題:咱們須要設計若是沒有登入話跳轉登入頁面,若是已經登入過則跳轉home頁面。
首先這個問題須要分2中狀況進行考慮:一、客戶進行訪問時是基於登入的前提下進行的,如管理系統等;二、這個系統不須要一開始就進行登入,只有在特殊的頁面才須要進行登入驗證,例如博客。
若是「這個系統不須要一開始就進行登入,只有在特殊的頁面才須要進行登入驗證」。能夠用vue-router的導航守衛來實現。導航守衛分爲:全局、單個路由和組件級的。關於各類級別的守衛在官網裏已經寫的很詳細了,能夠本身去看。在這裏咱們着重瞭解一下每一個守衛方法接收三個參數:to, from, next。
若是是第一種狀況,能夠用鉤子函數created進行判斷。登入的流程:git

  1. 判斷是否須要登入驗證,如何驗證?經過cookie獲取session。驗證不經過跳轉登入頁面。
  2. 點擊登入按鈕,先保證用戶名和密碼不爲空。而後用ajax請求後臺,驗證是否存在該用戶,密碼是否正確。若是正確則保存一些用戶的基礎信息和保存cookie。固然這裏也應考慮跨域的事情。
//這是一些路由代碼,下面的參數說明都是依照這段代碼的
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Login from '@/login'

Vue.use(Router)

const router = new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      redirect: '/home'   //重定向
    },
    {
        path: '/home',
        name: 'HelloWorld',
        component: HelloWorld
    },
    {
        path: '/login', 
        name: 'login',
        component: Login
    }
  ]
});
  1. to:即將進入的路由目標(例如你的地址是/home,那路由目標(to)中包括home的全部信息如name等)。具體to中包括的信息能夠參考路由信息對象。如何使用,好比咱們須要知道這個路由的名稱:to.name。
  2. from:與to正好相反,是當前導航正要離開的路由。由於2者都是路由對應因此他們包括的內容和用法都是同樣的,不同的只是他們表明的路由目標不同。例如你是從login到home的,那麼to表明home,from表明login。
  3. next:是一個函數,控制導航的跳轉或者終止。具體用法官網寫的是否清楚。
next: Function: 必定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。一、 next(): 進行管道中的下一個鉤子。若是所有鉤子執行完了,則導航的狀態就是 confirmed (確認的)。二、 next(false): 中斷當前的導航。若是瀏覽器的 URL 改變了(多是用戶手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。三、 next('/') 或者 next({ path: '/' }): 跳轉到一個不一樣的地址。當前的導航被中斷,而後進行一個新的導航。你能夠向 next 傳遞任意位置對象,且容許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。三、 next(error): (2.4.0+) 若是傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 註冊過的回調。 確保要調用 next 方法,不然鉤子就不會被 resolved

在vue中請額外注意箭頭函數的運用:注意箭頭行數this指向的是執行時的上下文。例如咱們在methods中定義一個方法,而後這裏面須要訪問data裏面的數據,若是你用同步的方法能夠用this.x訪問到data中的數據,由於這時候this指向的是vue中的上下文,其能夠訪問data定義的數據。若是該方法用箭頭函數定義且你在外邊調用,那麼這時候this指向的是執行時的上下文,這是this並不能訪問定義在vue內部的data。github

//html
<button class="btn loginBtn" data-loading-text="Loading..." type="button" @click="doLogin()"> LOGIN</button>

//js
<script>
export default {
  name: 'login',
  data () {
    return {
      msg: 'Welcome to Login page'
    }
  },
  methods: {
      doLogin() {
          var self = this;
          console.info(self.msg);//正常訪問,內容爲「Welcome to Login page」
      }
  }
}
//若是爲箭頭函數
export default {
  name: 'login',
  data () {
    return {
      msg: 'Welcome to Login page'
    }
  },
  methods: {
      doLogin:() => {
          var self = this;
          console.info(self.msg);//不能訪問,控制檯打印出「undefined」
      }
  }
}
</script>

3、全局守衛

前面咱們說到,在某些頁面須要進行登入驗證。這裏咱們能夠藉助router導航守衛中的全局守衛beforeEachweb

router.beforeEach((to, from, next)=> {
    ......
})

但這裏咱們須要區別跳轉頁面時,那個頁面是須要驗證的那個是不要的。咱們能夠直接在定義路由的時候就指出須要驗證的地址,設置meta:{requireAuth: true },而後在beforeEach中判斷to.meta.requireAuth是否爲true,若是爲true則須要驗證。代碼以下:

//router文件下的index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'//這裏特別說明一下@這個東西,在webpack配置文件中的resolve下面alias設置了@表明‘src’路徑。具體webpack配置的問題我在前面的幾章也說過。
import Login from '@/login'

Vue.use(Router)

const router = new Router({
    mode: 'history',
  routes: [
    {
      path: '/',
      redirect: '/home'   //重定向
    },
    {
      path: '/home',
      name: 'HelloWorld',
      component: HelloWorld,
      meta:{requireAuth: true }
    },
    {
        path: '/login', 
        name: 'login',
        component: Login
    }
  ]
});
router.beforeEach((to, from, next)=> {
    if(to.meta.requireAuth) {
        ....
    }
    next();
})
export default router;

而後知道那個頁面須要驗證後咱們須要經過cookie來獲取session判斷是否已經登入過。首先咱們須要封裝一下cookie。在src下新建文件夾util,在util下新建cookie.js

//獲取cookie
export function getCookie(name) {
    var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)")
    if(arr = document.cookie.match(reg)) {
        return arr[2];
    }else {
        return null;
    }
}

//設置cookie
export function setCookie(c_name, value, expiredays) {
    const exdate = new Date();
    exdate.setDate(exdate.getDate() + expiredays);
    document.cookie = c_name + '=' + escape(value) + ((expiredays == null) ? "": ';expires='+exdate.toGMTString())
}

//刪除cookie
export function delCookie (name) {
    var exp = new Date();
    exp.setTime(exp.getTime() - 1);
    var cval = getCookie(name);
    if (cval != null)
      document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
}

而後在router/index.js引入cookie.jsimport {delCookie,getCookie} from '@/util/cookie',最終router/index.js變成

......
import {delCookie,getCookie} from '@/util/cookie'
......

router.beforeEach((to, from, next)=> {
    if(to.meta.requireAuth) {
        if(getCookie('session')) {
            next();
        }else {
            next({ path: '/login' });
        }
    }
    next();
})

而後在main.js中引入router

import Vue from 'vue'
import App from './App'
import router from './router'
import 'bootstrap/css/bootstrap.min.css'//這裏的bootstrap同上的@
import 'bootstrap/js/bootstrap.min.js'

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

4、登入頁面

我默認你知道如何操做vuex,若是不知道的能夠看這裏,我以爲寫得十分通俗易懂,我就不具體說了。在src下建好store以後須要在main.js中引用並使用。

//main.js
......
import store  from '@/store'
......

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})
//login.vue    html
<template>
  <div class="login">
      <div class="logo">
          <img src="./assets/bog-logo.png" height="200px">
          <h1>{{ msg }}</h1>
      </div>
    <div class="content">
        <form class="loginForm">
      <input type="text" v-model= "loginForm.userName" class="form-control" placeholder="Plass enter your username">
      <input type="password" v-model= "loginForm.password" class="form-control" placeholder="Plass enter your password">
      <button class="btn loginBtn" data-loading-text="Loading..." type="button" @click="doLogin()"> LOGIN</button>
      <button class="btn enrollBtn" data-loading-text="Enroll..." type="button"> 註冊</button>
        </form>
    </div>
    </div>
</template>

<style scoped>
    .login {
        width: 100%;
        height: 100%;
  }
  .logo {color: #53538f;}
    .content {margin:0 auto;background: #e2e2f6;width: 50%;;max-width: 500px;padding: 30px;
                        border-radius: 10px}
    .loginForm {margin:0 auto;}
    .loginForm .form-control {margin:10px 0;height: 50px;}
    .loginForm .loginBtn,.enrollBtn {background: #53538F;color: #fff;font-size: 20px;}
    .enrollBtn {background: #f5c7bc;font-size: 12px;}
</style>
//js
export default {
  name: 'login',
  data () {
    return {
      msg: 'Welcome to Login page',
      loginForm: {
          userName: '',
          password: ''
      }
    }
  },
  methods: {
      doLogin() {
          const _self = this;
          const loginForm = _self.loginForm;
          const api = _self.$store.state.api;//這裏的api就是定義在vuex中,這樣全部的組件均可以訪問,而且之後修改的話只須要修改一處就行
          if(loginForm.userName == '' || loginForm.password == '') {
              alert("用戶名或密碼不能爲空!")
              return ;
          }
          $(".loginBtn").button('loading');
      $(".enrollBtn").hide(500);
      $.post(api + '/doLogin', loginForm, function(result){
          if(!result.success) {
              alert(result.msg);
              $(".loginBtn").button('reset');
              $(".enrollBtn").show(500);
              return ;
          }
      });
      }
  }
}
</script>

這裏我是用node作了一個簡單的後臺服務器,因此涉及到跨域的問題,我先用了極其簡單的方法解決。後臺代碼都放在了serve文件夾中。在app.js中設置header和訪問路徑,並返回結果。

..........

//設置跨域訪問
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By",' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

........
app.post('/doLogin',function(req,res){
    const user = {
        "admin":'123456'
    };
    const vUser = req.body;
    var result = {};
    if(!user.hasOwnProperty(vUser.userName)) {
        result["success"] = false
        result["msg"] = "不存在該用戶!"
    }else {
        result["success"] = true
        result["msg"] = "登入成功!"
        if(user[vUser.userName] != vUser.password){
            result["success"] = false
            result["msg"] = "密碼不正確!"
        }
    }
    res.send(result);
}) ;

登入成功後,利用crypto加密用戶名,生成token返回前端,存儲到cookie中。由於模塊有可能會比較多,因此我就將user模塊分離出來了。最後服務器代碼,先在serve下建一個名叫routes的文件夾,全部路徑都放在這裏。先建一個index.js

const photos = require('./photos');
const user = require('./user');//引入user相關的操做
/*
 * GET home page.
 */

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

/*
 * GET photo page.
 */
exports.photos = photos;

exports.user = user;

具體的crypto屬性方法能夠參考官網。但這裏我發現一個問題,我也不知道爲何。若是createHmac寫在上頭第二次操做的時候就會報「HashUpdate fail」。若是有知道的請告知我一下。

//新建user.js文件
const crypto = require('crypto');//nodejs的內置模塊 加密
//const hmac = crypto.createHmac('sha256', 'blogsss');若是這樣寫,再第二次操做的時候就會報」HashUpdate fail「
/*
 * GET users listing.
 */
const user = {
    "admin":'123456'
};

exports.doLogin = function(req, res, next){
  const vUser = req.body;
    var result = {};
    if(!user.hasOwnProperty(vUser.userName)) {
        result["success"] = false
        result["msg"] = "不存在該用戶!"
    }else {
        result["success"] = false
        result["msg"] = "密碼不正確!"
        if(user[vUser.userName] == vUser.password){
            //const hash = hmac.update(vUser.userName+"date="+new Date()).digest('hex');
            const hash = crypto.createHmac('sha256', 'blogsss').update(vUser.userName+"date="+new Date()).digest('hex');
            result["success"] = true
            result["msg"] = "登入成功!"
            result["token"] = hash
        }
    }
    res.send(result);
};

登入成功以後除了要將返回的token存儲之外還需將vuex的一些用戶信息修改,而且跳轉home頁面。這裏須要修改的時候,要在login頁面引入3個mapGetters, mapMutations, mapActions。但這裏我只須要mapActions因此只引入了mapActions。

//login.vue
<script>
import {mapActions} from 'vuex';
import {getCookie, setCookie} from '@/util/cookie'
export default {
 ....
  methods: {
      ...mapActions([
      'updateUserInfo'//注意這裏須要加單引號,以前參考的文檔中沒有,因此一直報updateUserInfo未定義的錯誤
    ]),
      doLogin() {
          const _self = this;
          const loginForm = _self.loginForm;
          const api = _self.$store.state.api;
          if(loginForm.userName == '' || loginForm.password == '') {
              alert("用戶名或密碼不能爲空!")
              return ;
          }
          $(".loginBtn").button('loading');
      $(".enrollBtn").hide(500);
      $.post(api + '/doLogin', loginForm, function(result){
          if(!result.success) {
              alert(result.msg);
              $(".loginBtn").button('reset');
              $(".enrollBtn").show(500);
              return ;
          }else {
              setCookie("session", result.token, 2);
              _self.updateUserInfo({//修改vuex的值
                  name:loginForm.userName,
                  login:true
              });
              _self.$router.push('/home');
          }
      });
      }
  }
}
</script>

這裏須要注意的是vuex的actions,它只能傳一個參數,若是有2個後面的就undefined了,因此我這裏解決的方法是傳一個對象進去。

//store/actions.js
import * as types from './mutation-type.js';

export default {
    updateUserInfo({commit}, obj) {
        commit(types.SET_USERNAME, obj.name);
        commit(types.SET_LOGIN, obj.login);
    }
};

這樣該存的存,該改的改以後用this.$router.push('/home')咱們跳轉到home頁面了。


參考連接

  1. 導航守衛:https://router.vuejs.org/zh-c...
  2. 路由信息對象:https://router.vuejs.org/zh-c...
  3. cookie:http://www.jb51.net/article/1...
相關文章
相關標籤/搜索