【Vue.js】vue用户登录功能

之前用vue实现一个网站的登录功能,这里做一个记录和总结,以下是需要实现的登录业务描述:

1.输入用户名和密码,点击登录按钮,若两者匹配,即可进入首页,首页展示登录用户的信息;

2.当用户登录后,无法访问登录页;

3.用户未登录,无法访问需要登录才能展示的页面,此时需要跳转到登录页面,当登录成功后,直接跳转到刚才用户点击访问的页面。

一.基本的登录功能实现

首先简单介绍登录的逻辑,顺序图如下:

登录按钮绑定点击事件handleLogin,先要做前端验证,就是检查用户名和密码的规范性,可以按照需求具体问题具体分析,比如做正则判断,或者发送请求验证是否存在等,这里不详细实现了,仅判断用户是否输入,之后派发vuex,action为userLogin,login.vue的主要代码如下:

data(){
      return {
        phone:'',
        password:''
      }
    },
async handleLogin(){
  const {phone,password} = this;
  try {
     phone && password && await this.$store.dispatch('userLogin',{phone,password});
     let toPath = this.$route.query.redirect || 'home';
     this.$router.push(toPath);
  } catch (error) {
     alert(error.message);
  }
}

在userLogin里,调用封装好的reqLogin方法向后端接口发送登录请求,该请求的响应结果为两种类型,如果用户名和密码正确,返回token(验证用户信息的字符串),若登录失败,返回失败的原因。

这里使用async和await修饰符处理异步请求,async修饰的方法userLogin会返回一个Promise实例,这里可以通过userLogin返回的Promise是成功或失败告知login.vue组件登录行为的结果。当登录成功时,首先让vuex保存token,但是vuex数据刷新后会丢失,所以还需要将token信息保存在浏览器的本地存储中,返回一个非Promise的类型。登录失败,返回一个失败的Promise,并将失败的原因作为异常抛出。

随后在登录组件login.vue中,判断userLogin的返回结果,由于使用到了await修饰符处理异步操作,且它只能获取到成功的Promise,所以这里使用try...catch...获取await的失败结果,当登录失败时将原因弹出在页面上。

成功后使用$router.push(toPath),跳转至home首页。

vuex代码:

import {reqGetCode,reqRegister,reqLogin,reqUserInfo,reqUserLogout} from '@/api'
const state = {
    code:'',
    // vue仓库存储数据不是持久化的,刷新数据丢失
    token:localStorage.getItem('TOKEN') || '',
    userInfo:{}
};
const mutations = {
    USERLOGIN(state,token){
        state.token = token;
    },
    USERINFO(state,userInfo){
        state.userInfo = userInfo || {};
    },
};
const actions = {
    async userLogin(context,data){
        // 发送登录请求
        let result = await reqLogin(data);
        if(result.code === 200) {
            // 存放token,vuex和localStorage各一份
            context.commit('USERLOGIN',result.data.token);
            localStorage.setItem('TOKEN',result.data.token);
            return 'ok';
        } else {
            // 登录失败
            return Promise.reject(new Error(result.message));
        }
    },
}

token即令牌,是服务器识别登录用户信息的有效凭证,在登录成功时由服务器返回前端,它一般存放在本地存储中,并在登陆后的请求中放在请求头中一起提交服务器,从而让服务器验证登录用户身份的合法性。当跳转至首页后,就需要发请求获取用户信息了,此时就需要将刚才获取的token提交服务器了,需要对axios请求做二次封装,如下:

import axios from 'axios'
// 引入进度条
import nprogress from 'nprogress'
// 引入进度条的样式
import 'nprogress/nprogress.css'
// 引入store
import store from '@/store'
// 创建axios实例
const requests = axios.create({
    // 配置对象
    // 基础路径
    baseURL:"/api",
    // 请求超时的时间
    timeout:5000,
});

// 请求拦截器 -- 在请求发出前做些事情
requests.interceptors.request.use((config) => {
    // 需要携带token给服务器
    if(store.state.user.token) {
        config.headers.token = store.state.user.token;
    }
    nprogress.start();
    // config 是配置对象,里面包含请求头headers
    return config;
});

// 响应拦截器 
requests.interceptors.response.use((res) => {
    nprogress.done();
    return res.data;
},(err) => {
    return Promise.reject(new Error('fail'))
});

export default requests;

核心就是在axios的请求拦截器中给登录后的每次请求添加请求头tokenconfig.headers.token = store. state. user.token

在首页发送请求

<template>
    <p v-if="!userName">
        <span>请</span>
        <router-link to="/login">登录</router-link>
        <router-link to="/register" class="register">免费注册</router-link>
    </p>
    <p v-else>
        <a href="#">{
   
   {userName}}</a>
        <a href="#" class="register" @click="logout">退出登录</a>
    </p>
</template>


<script>
export default {
  name:"home",
  mounted(){
    this.getUserInfo();
    
  },
  computed:{
    userName(){
      let userInfo = this.$store.state.user.userInfo;
      return userInfo.name || '';
    }
  }
  methods:{
    getUserInfo(){
        try {
          await this.$store.dispatch('getUserInfo');
        } catch (error) {
          // alert(error.message);
        }
    }
  }
}
</script>

vuex:

const state = {
    userInfo:{}
};
const mutations = {
    USERINFO(state,userInfo){
        state.userInfo = userInfo || {};
    },
};   
const actions = {
    // 获取用户信息(token)
    async getUserInfo(context){
        let result = await reqUserInfo();
        if(result.code === 200) {
            context.commit('USERINFO',result.data);
            return 'ok';
        } else {
            return Promise.reject(new Error(result.message));
        }
    },
}

二.使用路由守卫做未登录拦截

以上实现了基本登录的功能,但依旧要进行一些优化,比如登录后访问登录页面需要跳转,以及未登录访问用户信息相关的页面的拦截的功能,这里就需要使用到vue-router的路由全局守卫了, 全局守卫的配置一般实在路由配置文件中进行的,一般是项目中routes/index.js文件中。

需要做的就是在路由跳转之前使用全局前置守卫router.beforeEach,对跳转的页面和登录状态做判断,具体判断的逻辑如下:

 routes/index.js:

import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
import store from '@/store'

// 使用路由插件
Vue.use(VueRouter);
// 配置路由
const router = new VueRouter({
    // 配置路由
    routes,
    // 滚动行为
    scrollBehavior(to, from, savedPosition) {
        // return 期望滚动到哪个的位置
        // y:0代表垂直滚动条在最上方
        return {y:0};
    }
});
// to:获取到跳转的那个路由信息;
// from:获取到哪个路由而来的信息;
// next:放行函数,
// 	next('/login'):放行到指定的路由
// 	next(false):返回到起始的路由
router.beforeEach(async (to,from,next) => {
    let token = store.state.user.token;
    if(token) {
        // 登录
        if(to.path === '/login') {
            // 登陆后访问登录页
            next({path:from.path});
        } else {
            // 登陆后访问非登录页
            if(!store.state.user.userInfo.name) {
                // 当空对象作为判断条件时,相当于true。当空对象与布尔值直接比较时,相当于true,但空数组与布尔值直接比较时是false
                // 登录后访问非登录页,如果此时用户信息由于页面刷新,store为空需要再次请求后端,拿到用户信息
                try {
                    await store.dispatch('getUserInfo');
                    next();
                } catch (error) {
                    // token过期
                    await store.dispatch('userLogout');
                    next({path:'/login'});
                }
            } else {
                // 登录后访问非登录页,无刷新操作
                next();
            }
        }
    } else {
        // 未登录
        if(to.path.indexOf('/pay') ==! -1 || to.path.indexOf('trade') !== -1 || to.path.indexOf('center') !== -1) {
            next(`/login?redirect=${to.path}`);     
        } else {
            next();
        }
    }
});

export default router;

这里还需要注意一点,就是当用户处于未登录状态下,点击需要登录才能访问的页面时,会跳转登录页,但登录成功后需要直接跳转到刚刚点击的那个页面而不是首页,所以这里在跳转登陆页时通过query传参的方式,即next(`/login?redirect=${to.path}`)将跳转的路由路径to.path传递给登录页,在成功登录后使用编程式路由导航this.$router.push(this.$route.query.redirect)跳转。

猜你喜欢

转载自blog.csdn.net/weixin_43655896/article/details/122787629