在实际项目中,经常会有要求用户登录才能访问其他页面的场景,当用户在未登录的情况下去访问内容页面的地址时,我们需要强制跳转到登录页面,同样的,当用户登录成功了但又去访问登录页面的地址时,我们应该强制跳转到主页面。
这篇文章和大家一起讨论一下这种业务场景如何用vue去实现。
上面的场景提到了页面之间的跳转,因此我们在项目中引入vue-router,然后通过vue-router的导航守卫来实现上面的需求。
Vue Router 是Vue.js官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。
这里假设我们的页面有两个:
- 主页(登录后可见)Home.vue
- 登录页 Login.vue
我们的路由配置如下
export default new Router({
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/login',
name: 'login',
component: Login
}
]
})
通过路由我们可以看出这两个页面的访问地址分别如下:
在实际情况中,我们通常会把主页的地址和域名进行绑定,也就是说用户不管是否登录,首次访问的肯定是主页的地址。因此我们需要在首页上判断登录状态然后决定是否重定向到登录页。
但是如果把登录状态判断的业务写在首页上,意味着你每添加一个新的页面都要再写一次重复的逻辑,很显然,这不利于维护。好在vue-router具有导航守卫的特性,因此我们为router添加全局前置守卫,在main.js文件中,我们把守卫代码写在Vue实例化的上方,代码如下:
router.beforeEach((to, from, next)=>{
//
})
这样一来,页面每次发生跳转都会触发这里的钩子函数,如果想让跳转继续,就调用next(),要么就对页面进行重定向。
我们这里讨论的场景是要判断是否登录,登录状态应该是一个全局状态,因为我们可能会在任意页面上访问它,也会在登录页面上改写它。因此我们需要在项目里引入vuex。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
我们在store.js里添加登录状态这个state,命名为islogin
export default new Vuex.Store({
state: {
islogin: false //是否已登录
},
mutations: {
loginSuccess(state){ //登录成功,改变登录状态为true
state.islogin = true;
}
},
actions: {
}
})
我们还添加了一个名为loginSuccess的mutation,当登录成功的时候,我们需要提交这个mutation来改变全局登录状态,这时,凡是引用了state里islogin这个属性的任何页面或者组件,都会同步改变状态,这就是使用vuex的好处。在这里我们不对vuex进行过多讨论。
继续回到我们的业务场景,我们要在全局前置守卫里判断登录状态然后控制导航继续或者重定向,代码如下:
router.beforeEach((to, from, next)=>{
let logined = store.state.islogin;
if(to.name=="login"){
if(!logined){
next();
}else{
router.replace("/");
}
}else{
if(!logined){
router.replace({path:"login", query:{redirect:to.name}});
}else{
next();
}
}
})
这段代码的第一行我们从store里获取了登录状态islogin,然后需要判断页面去向(to.name),如果页面前往非登录页(主页或其他页面,代码中的else逻辑),我们判断是否已登录,是就next()继续,否则就把页面重定向到登录页。这里有一个细节,我们用了编程式导航的replace方法,它和push的区别是,页面重定向以后无法返回上一级,我们这里的场景的确不希望用户返回上一级,因此使用了replace。
还有一个需要注意的地方,我们登录成功后肯定希望页面重新跳转到原本的去向,因此我们把to.name作为一个查询参数传递给登录页面 query:{redirect:to.name},在登录页面登录成功后,我们就可以获取这个redirect然后重新跳转。
如果页面前往的是登录页(to.name=="login"),我们判断是否已登录,未登录就next()继续,否则就把页面重定向到主页(或其他),这里我们依然用了replace,因为我们还是不希望用户可以返回这一层。
现在还有一个工作要做,就是在登录页面上改写全局登录状态,实际业务中大多都是调用后台登录接口,然后在登录成功后进行state的改写,以下代码我们用setTimeout模拟接口请求
setTimeout(()=>{
this.$store.commit("loginSuccess");
let redirect = this.$route.query.redirect; //获取redirect
if(redirect != undefined){
this.$router.replace({name:redirect})
}else{
this.$router.replace("/")
}
}, 1000);
以上代码模拟了登录成功之后的业务,第一行是提交了一个mutation来改写全局登录状态。第二行的redirect就是我们上面说的登录之前原本的目标页面,如果不存在这个参数,那就重定向到一个默认的页面(通常是主页)。现在由于全局登录状态已经为true,因此就可以顺利打开其他页面了。
这个案例到这里就结束了吧?
并没有!如果你已经按我上面的示例写了一个demo,登录成功并打开了主页,那么现在请刷新一下页面,怎么样?是不是又跳回登录页面了?
原因很简单,vuex只保存运行时的状态,它并不能实现数据持久化,刷新页面、重启浏览器、重启电脑这些操作都将导致整个vue单页面应用重新初始化,vuex也不例外。但我们的登录状态实际上肯定是需要保持一定时间的,不能每刷新一次就要登录一次。因此,要想做本地数据持久化,只有一个办法,要么用cookie要么用localStorage,在这里我们用后者,在刚才登录成功的业务里对状态进行本地保存。
localStorage.setItem("islogin", "true");
最后我们在main.js里面获取这个本地状态,如果本地状态是已登录,就提交mutation到store来改变全局登录状态。记得,这部分代码要写在全局前置守卫之前。
let islogin = localStorage.getItem("islogin"); //获取本地登录状态
if(islogin){
store.commit("loginSuccess")
}
router.beforeEach((to, from, next)=>{
let logined = store.state.isLogin;
//......
好了,关于强制登录的一个实现思路就这些了,大家有任何问题或想法请在评论区留言,我会一一回复。