vue设计个人博客-登入(起始)

githup webpack配置

一、webpack引入bootstrap

首先bootstrap是依赖于jquery的,所以需要安装jquery。
1、在下载好jquery之后,需要引入jquery。
在webpack.base.conf.js模块中,使用webpack的内置模块ProvidePlugin,可以自动加载模块,而不需要使用import等。

const webpack = require('webpack')

plugins: [
    new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery"
    })
]
//添加好之后,可以直接找一个页面测试
//例在HelloWorld.vue添加,审查元素,body中有没有添加成功
//如果成功者说明引入成功
$("body").append("<div>1</div>");

2、安装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安装一下就可以了。

刚刚不是说也可以直接在官网上下载下来的,如果是直接下载的。将下载包移动到你放置静态文件的文件夹中解压出来(我比较推荐这种做法,因为这样比较好管理),一般不会怎么变化的静态文件我会存放在“static”目录下。

//在`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'

到此开发时的基本样式框架已经准备完毕。

二、登入页面

最终结果:《vue设计个人博客-登入(起始)》

问题:我们需要设计如果没有登入话跳转登入页面,如果已经登入过则跳转home页面。
首先这个问题需要分2中情况进行考虑:1、客户进行访问时是基于登入的前提下进行的,如管理系统等;2、这个系统不需要一开始就进行登入,只有在特殊的页面才需要进行登入验证,例如博客。
如果“这个系统不需要一开始就进行登入,只有在特殊的页面才需要进行登入验证”。可以用vue-router的导航守卫来实现。导航守卫分为:全局、单个路由和组件级的。关于各种级别的守卫在官网里已经写的很详细了,可以自己去看。在这里我们着重了解一下每个守卫方法接收三个参数:to, from, next。
如果是第一种情况,可以用钩子函数created进行判断。登入的流程:

  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 方法的调用参数。1、
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。2、
next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。3、
next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。3、
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。

//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>

三、全局守卫

前面我们说到,在某些页面需要进行登入验证。这里我们可以借助router导航守卫中的全局守卫beforeEach

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/>'
})

四、登入页面

我默认你知道如何操作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…
    原文作者:shishans
    原文地址: https://segmentfault.com/a/1190000014896001
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞