完成element的引入设计完成Login.vue页面后就该通过使用vuex前端路由来进行登录拦截
一、前端路由
在第二篇中曾说过#
号是使用Hash
的模式,我们可以通过相关配置来去掉
这里#
号称为锚点这里可以使得URL 发生了变化,但页面不会跳转。
这个办法可以缓解后端服务器的压力,利用Ajax可以不重载页面就刷新数据,加上#
号的特性------改变URL不请求后端,可以实现前端页面的整体变化而不去请求后端
#
号后面的地址称为Hash
另一种方式为History
模式,这种方式使用了History API
—这种模式是很对历史记录的Api,原理是把这个页面保存在一个对象里(state),当页面的URL变化找到对象的对象,然后还原这个页面
History
解决#
号实现前端路由
二、使用History模式
从默认的 Hash
模式切换为 History
模式只需修改路由配置即可。在 src\router.js
加入mode: 'history'
即可如图所示
运行项目,访问不加#
号的 http://localhost:8080/login ,成功加载页面。
三、Vuex与前端登录拦截器
练习项目为前后端分离项目所以这次不使用后端拦截
实现前端登录器,需要在前端判断用户的登录状态。
实现方式:
- 在组件中的data中设置一个状态标志
- 通过全局属性来Vuex实现
1、引入Vuex
在项目文件夹中运行npm install vuex --save
,在src目录下创建一个文件夹并创建store文件夹,并创建index.js------src\store\index.js
来进行vue和vuex的使用如图所示
在 index.js 里设置我们需要的状态变量和方法。为了实现登录拦截器,我们需要一个记录用户信息的变量。为了方便日后的扩展(权限认证等),我们使用一个用户对象而不是仅仅使用一个布尔变量。同时,设置一个方法,触发这个方法时可以为我们的用户对象赋值。完整的代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
/*
* localStorage本地存储
* 在项目打开的时候会判断本地存储中是否有 user 这个对象存在,
* 如果存在就取出来并获得 username 的值,否则则把 username 设置为空。
* 这样我们只要不清除缓存,登录的状态就会一直保存。
*/
state: {
user: {
username: window.localStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('user' || '[]')).username
}
},
mutations: {
login (state, user) {
state.user = user
window.localStorage.setItem('user', JSON.stringify(user))
}
}
})
2、修改路由配置
区分页面是否拦截,需要修改src\router.js
,在需要拦截的路由中加一个数据设置一个requireAuth
属性
requireAuth
属性作用是表明该路由是否需要登录验证,在进行全局拦截时,我们将通过该属性判断路由的跳转,该属性包含在meta属性中:
完整的index.js代码如下
import Vue from "vue";
import Router from "vue-router";
import AppIndex from "@/views/AppIndex";
import Login from "@/views/Login";
Vue.use(Router);
export default new Router({
routes: [
{
path: "/login",
component:Login
},
{
path: "/index",
component: AppIndex,
meta:{
requireAuth: true
}
}
]
});
3、使用钩子函数判断是否拦截
钩子函数及在某些时机会被调用的函数。这里我们使用 router.beforeEach(),意思是在访问每一个路由前调用。
打开src\main.js
,首先添加对store
的引用
import store from './store'
并修改Vue对象内容
new Vue({
components:{App},
router,
store,
render: h => h(App),
}).$mount('#app')
接着写 beforeEach()
函数
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) {
if (store.state.user.username) {
next()
} else {
next({
path: 'login',
query: {redirect: to.fullPath}
})
}
} else {
next()
}
}
)
l逻辑如下:
1、首先判断访问的路径是否需要登录
2、如果需要判断Store里有没有存储user的信息,存在即放行并存储访问的页面路径(方便以后跳转)
完整的main.js代码
import Vue from 'vue'
import App from './App.vue'
import router from "./router"
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import store from './store'
var axios = require('axios')
axios.defaults.baseURL = 'http://localhost:8888/api'
Vue.prototype.$axios = axios
Vue.config.productionTip = false
Vue.use(ElementUI)
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) {
if (store.state.user.username) {
next()
} else {
next({
path: 'login',
query: {redirect: to.fullPath}
})
}
} else {
next()
}
}
)
new Vue({
components:{App},
router,
store,
render: h => h(App),
}).$mount('#app')
4、修改Login.vue
之前的登录组件中,我们只是判断后端返回的状态码,如果是 200,就重定向到首页。在经过前面的配置后,我们需要修改一下登录逻辑,以最终实现登录拦截。
逻辑如下:
1、点击登录按钮,向后端放松数据
2、受到后端返回的成功代码时,触发 store
中的login()
方法,把 loginForm
对象传递给 store
中的user
对象**(简单实现)**
修改后的login()方法
login () {
var _this = this
console.log(this.$store.state)
this.$axios
.post('/login', {
username: this.loginForm.username,
password: this.loginForm.password
})
.then(successResponse => {
if (successResponse.data.code === 200) {
// var data = this.loginForm
_this.$store.commit('login', _this.loginForm)
var path = this.$route.query.redirect
this.$router.replace({path: path === '/' || path === undefined ? '/index' : path})
}
})
// eslint-disable-next-line no-unused-vars
.catch(failResponse => {
})
}
}
完整Login.vue代码
<template>
<body id="poster">
<el-form class="login-container" label-position="left"
label-width="0px">
<h3 class="login_title">系统登录</h3>
<el-form-item>
<el-input type="text" v-model="loginForm.username"
auto-complete="off" placeholder="账号"></el-input>
</el-form-item>
<el-form-item>
<el-input type="password" v-model="loginForm.password"
auto-complete="off" placeholder="密码"></el-input>
</el-form-item>
<el-form-item style="width: 100%">
<el-button type="primary" style="width: 100%;background: #505458;border: none" v-on:click="login">登录</el-button>
</el-form-item>
</el-form>
</body>
</template>
<script>
export default {
name: "Login",
data () {
return {
loginForm: {
username: '',
password: ''
},
responseResult: []
}
},
methods: {
login () {
// this.$axios
// .post('/login', {
// username: this.loginForm.username,
// password: this.loginForm.password
// })
// .then(successResponse => {
// if (successResponse.data.code === 200) {
// this.$router.replace({path: '/index'})
// }
// })
// // eslint-disable-next-line no-unused-vars
// .catch(failResponse => {
// })
// }
// }
var _this = this
console.log(this.$store.state)
this.$axios
.post('/login', {
username: this.loginForm.username,
password: this.loginForm.password
})
.then(successResponse => {
if (successResponse.data.code === 200) {
// var data = this.loginForm
_this.$store.commit('login', _this.loginForm)
var path = this.$route.query.redirect
this.$router.replace({path: path === '/' || path === undefined ? '/index' : path})
}
})
// eslint-disable-next-line no-unused-vars
.catch(failResponse => {
})
}
}
}
</script>
<style>
#poster{
background:url("../assets/login.jpg") no-repeat;
background-position: center;
height: 100%;
width: 100%;
background-size: cover;
position: fixed;
}
body{
margin: 0px;
}
.login-container {
border-radius: 15px;
background-clip: padding-box;
margin: 90px auto;
width: 350px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.login_title {
margin: 0px auto 40px auto;
text-align: center;
color: #505458;
}
</style>
5、检验效果
运行前端项目,访问http://localhost:8080/#/index后页面直接拦截跳转
http://localhost:8080/#/login?redirect=%2Findex如果所示