Vue2电商平台(六)、注册登录,请求头配置token,token持久化存储;导航守卫(重点);组件内守卫、路由独享守卫

news/2024/10/7 19:39:34/

文章目录

  • 一、注册
    • 1. 获取注册验证码
    • 2. 完成注册用户
  • 二、登录
    • 1. 登录获取token
    • 2. Home首页携带token获取用户数据
    • 3. 持久化存储token
    • 4. 退出登录
    • 5. 导航守卫 (牛)
    • 6. 路由独享守卫beforeEnter
    • 7. 组件内守卫(用的很少)

一、注册

1. 获取注册验证码

本系统:点击按钮,发送请求,后台返回生成的验证码。(正常的业务逻辑是:点击按钮,后台根据手机号给手机发送验证码,用户输入验证码。)
在这里插入图片描述

javascript">1. 接口
// 获取验证码// /api/user/passport/sendCode/{phone}
export const reqRegisterCode = function (phone) {return requests.get(`/user/passport/sendCode/${phone}`);
}
javascript">2. vuex小仓库
// 新建user小仓库,用来存储登录注册的信息
import { reqRegisterCode, registUser } from '@/api'
export default {namespaced: true,state: {code: ''},actions: {// 获取验证码async getCode (context, phone) {let res = await reqRegisterCode(phone)if (res.code === 200) {context.commit('GETCODE', res.data)return 'ok'} else {return Promise.reject(new Error('faile'))}},mutations: {GETCODE (state, data) {state.code = data}},getters: {},
}
javascript">3. Register(注册)组件内
async getRegisterCode () {try {//1. 发请求 来个逻辑短路判断手机号是否输入this.phone && (await this.$store.dispatch('user/getCode', this.phone))// 2. 获取到验证码,方式一: this.code = this.$store.state.user.code// 方式二是用computed ...mapState('user', ['code'])} catch (error) {alert(error.message)}
},

v-model将验证码code双向绑定到验证码的输入框里。当后台返回验证码,组件内读取到验证码时,验证码就自动输入到input框里。

2. 完成注册用户

拿着手机号、密码、验证码去发请求,将该用户添加到数据库中。

javascript">1. 接口
// 注册用户   /api/user/passport/register  请求方法 post 
// 参数 phone:手机号,password:密码; code:验证码
export const registUser = (phone, password, code) => {return requests({url: `/user/passport/register`,method: 'post',data: {phone, password, code}})
}
javascript">2.vuex
aciotns:{// 注册用户async sendRegister (context, { phone, password, code }) {let res = await registUser(phone, password, code)// 223 注册失败console.log('注册结果', res);if (res.code === 200) {return 'ok'} else {// 不同类型的失败,在res.message中都会给出解释return Promise.reject(new Error(res.message))}}
}

在这里插入图片描述
注册成功则跳转到登录页面

javascript">3. 组件
data () {return {phone: '',      // 电话号码code: '',      //验证码password: '',      // 登录密码rePassowrd: '',      // 确认密码isAgree: true      // 是否同意}
}
methods:{async registUser () {console.log('注册用户');try {const { phone, code, password, rePassowrd, isAgree } = this// 逻辑与验证信息if (phone && code && password && rePassowrd && password == rePassowrd && isAgree == true) {await this.$store.dispatch('user/sendRegister', { phone, password, code })}// 注册成功路由需要进行跳转,跳转到登录页面this.$router.push({path: '/login',})} catch (error) {// 这里的message接收的是vuex中的Promise.reject()里的值alert(error.message)}}
}

在这里插入图片描述
这部分还缺一个表单验证,老师用的vee-validate。ElementUI有现成的表单验证,也可以用这个。本系统就先不弄表单验证了。

二、登录

1. 登录获取token

javascript">1. 登录接口
// 登录 /api/user/passport/login  请求:方法post
export const reqUserLogin = (phone, password) => {return requests({url: '/user/passport/login',method: 'post',data: {phone,password}})
}
javascript">2. vuex存储数据
state: {code: '', // 验证码token: ''},
actions:{// 登录async userLogin (context, { phone, password }) {let res = await reqUserLogin(phone, password)console.log('登录结果', res);// 服务器下发token作为用户的唯一标识,前端经常通过携带token找服务器要一些用户的数据进行展示if (res.code === 200) {context.commit('USERLOGIN', res.data.token)return 'ok'} else {return Promise.reject(res.message)}}
},mutations: {USERLOGIN (state, token) {state.token = token}
}

打印请求结果。登录成功的时候,后台为了区分用户是谁,服务器会下发token作为唯一标识符。这里的登录接口做的并不好,应该只返回token。
在这里插入图片描述
需要注意:vuex存储token并非持久化存储。

javascript">4. 组件发登录请求,成功则跳转home页面(暂定是home页面)
methods: {async Login () {console.log('登录');try {await this.$store.dispatch('user/userLogin', { phone: this.phone, password: this.pwd })//  成功则跳转到首页【结合导航守卫,这句话后续会变】this.$router.push('/home')} catch (error) {alert(error.message)}}
}

一般的业务逻辑为登录成功,服务器下发token,前端持久化存储token。前端找服务器要用户数信息进行展示时需要带着token

这里存在一个问题:登录成功之后一定是跳转到首页吗?具体看导航守卫,会回来填坑的。

2. Home首页携带token获取用户数据

登录成功,进入首页时,应该获取用户信息并展示在页面上。
在这里插入图片描述
(1) 获取用户信息的接口

javascript">// 获取用户信息 /api/user/passport/auth/getUserInfo
export const reqUSerInfo = () => {return requests({url: '/user/passport/auth/getUserInfo',method: 'get',})
}

(2). vuex三连环存储用户信息

javascript">state: {code: '',token: '',userInfo: {}
},
actions:{// 获取用户信息async getUserInfo (context) {let res = await reqUSerInfo()}
},
mutations:{GETUSERINFO (state, data) {state.userInfo = data}
}

(3). 请求拦截器携带token

javascript">// 2. 配置请求拦截器: 请求拦截器检测到请求,在请求发出去之前做一些事情
import store from '@/store'
requests.interceptors.request.use((config) => {// 进度条开始nProgress.start()// config:配置对象,其中header请求头属性很重要config.headers.userTempId = store.state.detail.userTempId// 配置token请求头if (store.state.user.token) {config.headers.token = store.state.user.token}return config;
})

Home组件一挂载完毕就发送获取用户信息的请求。

javascript">4. Home组件发请求
mounted () {// 发送请求,获取用户信息,【结合导航守卫,这格请求以后不在这里执行】this.$store.dispatch('user/getUserInfo')
},

在这里插入图片描述
如果不携带token,发送获取用户信息的请求时,服务器的返回结果是 未登录
在这里插入图片描述
(5). 将用户信息渲染到界面上

 <!-- 登录成功 则显示用户信息--><p v-show="userName"><span>{{ userName }}</span><router-link to="/login" class="register">退出登录</router-link></p><!-- 未登录则显示请登录等信息 --><p v-show="!userName"><span></span><router-link to="/login">登录</router-link><router-link to="/register" class="register">免费注册</router-link   ></p>
<script>javascript">computed: {userName () {return this.$store.state.user.userInfo.name}
}
</script>

在这里插入图片描述

3. 持久化存储token

当登录成功、进入首页、获取用户信息成功渲染到界面上后。此时如果刷新页面,会发现控制台仍旧报未登录的错误。这是因为,Vuex存储token并不是持久化存储。刷新页面,token数据清空,而Home页重新加载,仍旧发请求获取用户信息,没有token所以会报错。解决办法就是持久化存储token,即本地存储。

方式一:仓库中存储、仓库中读取
在这里插入图片描述
请求拦截器中读取仓库里的token,在请求头里加上token
在这里插入图片描述
方式二:仓库中存储,请求拦截器里读取
在这里插入图片描述
在这里插入图片描述
这里存在一个问题:首页发送获取用户信息的请求的前提是登录成功、跳转到首页。而在刚进入网站时,路由重定向的就是首页,所以刚进入网站时,就会有一个未登录的错误。(实际中,刚进入网页时不应该请求用户的信息。这个在导航守卫中被解决)

4. 退出登录

退出登录:,
(1)发请求,通知服务器退出登录
(2)清除项目当中的数据 userInfotoken

javascript">1. 接口
// 退出登录 /api/user/passport/logout
export const reqUserLogout = () => {return requests({url: '/user/passport/logout',method: 'get',})
}

此处注意action里面不能操作state,提交mutation修改state

javascript">2. vuex
actions: {// 退出登录async LogOut (context) {let res = await reqUserLogout()if (res.code === 200) {//action里面不能操作state,提交mutation修改statecontext.commit('CLEAR')return 'ok'} else {return Promise.reject(new Error('falie'))}}
},
mutations: {CLEAR (state) {// 清空用户数据state.userInfo = {}// 仓库里的token清空state.token = ''// 本地存储清空localStorage.removeItem('TOKEN')}
}
javascript">3. Header 组件内// 退出登录async userLogOut () {try {// 请求成功,则跳转页面await this.$store.dispatch('user/LogOut')// 回到首页this.$router.push('/login')} catch (error) {alert(error.message)}}

这里跳回首页会有一个bug

5. 导航守卫 (牛)

路由守卫的具体内容回顾这篇博客:Vue(十三) 路由守卫
导航:表示路由正在发生变化。导航守卫就是在进行路由跳转时进行的一些操作。

之前的程序存在的问题:
Q1: 用户在登录之后,地址栏中输入/login,仍旧能够进入登录页面。
Q2: 在刚进入网站时,重定向到首页,首页会请求用户信息,若此时还未登录,则没有token,请求会报错。
Q3: 由于获取用户信息的请求是在Home模块。假如进入search页面后,刷新页面,虽然有token,但是Search模块并不会发送请求,所以页面的用户信息还是会丢失。
Q4: 登录成功就一定跳转Home首页吗?

这些问题都是在路由变化的情况下发生的。导航守卫:

在这里插入图片描述
Q2,Q3----请求放在导航守卫里,不放在首页了。无论跳转哪个页面,即使是Search页面,如果没有用户信息,就会发起请求,获取数据并放到仓库重。Header组件再去读取仓库里存储的用户信息。

Q4----比如用户点击我的订单,但是此时未登录,则先去登录页面。登录成功之后应该直接跳转到我的订单页面,而不是首页。

javascript">// router/index.js
router.beforeEach(async (to, from, next) => {// 判断是否登录let token = localStorage.getItem('TOKEN')// 1. 已登录if (token) {// 2. 已登录,又要去登录或注册页面,不让去,就待在原来的页面if (to.path === '/login' || to.path === '/register') {next(false) // 取消当前的导航,url地址会重置到from路由对应的地址} else {// 2. 已登录,不去登录或注册页面,判断是否有用户信息// 3. 有用户信息,放行if (store.state.user.userInfo.name) {next()} else {// 3. 没有用户信息,发请求try {// 发送请求,获取用户信息await store.dispatch('user/getUserInfo')// 获取成功,放行next()} catch (error) {// 获取不成功,但是又登录了,说明token失效,则退出登录,并跳转到登录页面await store.dispatch('user/LogOut')next('/login')}}}} else {// 未登录访问, 交易相关(trade)、支付相关(pay,paysuccess)、用户中心(center)相关跳转到 登录页面; (此处感觉用路由meta元信息也可以)if (to.path.indexOf('/trade') != -1 || to.path.indexOf('/pay') != -1 || to.path.indexOf('/center') != -1) {// 跳到登录页面去,并携带这个想去的路由地址next('/login?redirect=' + to.path)} else {// 去的不是上面这些路由,而是home|search|shopCart等别的路由,放行next()}}
})

而在登录组件中,登录成功跳转的页面为:
在这里插入图片描述

未登录,点击我的订单,跳转到登录页面,此时路由信息为:
在这里插入图片描述

6. 路由独享守卫beforeEnter

当用户登录之后,还有如下几条规则需要设置:
(1) 只有从购物车页面(shopCart)才能跳转到交易页面(创建订单)
(2) 只有从交易页面(创建订单)才能跳转到支付页面(pay)

javascript"> // 交易{name: 'trade',path: '/trade',component: Trade,beforeEnter (to, from, next) {/* 只能从购物车界面跳转到交易界面 */if (from.path == './shopCart') {next()} else {next(false)}},},// 支付{name: 'pay',path: '/pay',component: Pay,/* 只能从交易界面跳转到支付界面 */beforeEnter (to, from, next) {if (from.path == './trade') {next()} else {next(false)}}}

7. 组件内守卫(用的很少)

熟悉一下这几个API

javascript">export default {beforeRouteEnter(to, from) {// 在渲染该组件的对应路由被验证前调用// 不能获取组件实例 `this` !// 因为当守卫执行时,组件实例还没被创建!console.log(this) //undefined},beforeRouteUpdate(to, from) {// 在当前路由改变,但是该组件被复用时调用// 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,// 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。// 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`},beforeRouteLeave(to, from) {// 在导航离开渲染该组件的对应路由时调用// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`},
}

beforeRouteUpdate这个API测试了一下,当路径由http://localhost:8080/#/detail/12直接变为http://localhost:8080/#/detail/14(也就是后边参数改变)时,这个API会被调用。如果由详情页(detail/12)返回商品页再进入详情页(detail/14);这个API也不会被调用。

只有从支付页面(pay)才能跳转到支付成功页面(paysuccess)。

javascript">export default {name: 'PaySuccess',beforeRouteEnter (to, from, next) {if (from.path === './pay') {next()} else {next(false)}}
}

http://www.ppmy.cn/news/1535839.html

相关文章

Cocos_鼠标滚轮放缩地图

文章目录 前言一、环境二、版本一_code2.分析类属性方法详细分析详细分析onLoad()onMouseWheel(event)详细分析 总结 前言 学习笔记&#xff0c;请多多斧正。 一、环境 通过精灵rect放置脚本实现鼠标滚轮放缩地图。 二、版本一_code import { _decorator, Component, Node }…

[运维]2.elasticsearch-svc连接问题

Serverless 与容器决战在即&#xff1f;有了弹性伸缩就不一样了 - 阿里云云原生 - 博客园 当我部署好elasticsearch的服务后&#xff0c;由于个人习惯&#xff0c;一般服务会在name里带上svc&#xff0c;所以我elasticsearch服务的名字是elasticsearch-svc&#xff1a; [root…

leetcode99 恢复二叉搜索树

问题描述&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下&#xff0c;恢复这棵树 。 示例 1&#xff1a; 输入&#xff1a;root [1,3,null,null,2] 输出&#xff1a;[3,1,null,null,2] 解释&#x…

【数据结构与算法】排序算法

3.7 排序算法 概述 比较排序算法 算法最好最坏平均空间稳定思想注意事项冒泡O(n)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)Y比较最好情况需要额外判断选择O( n 2 n^2 n2)O( n 2 n^2 n2)O( n 2 n^2 n2)O(1)N比较交换次数一般少于冒泡堆O( n l o g n nlogn nlogn)O( n l o g n nlogn …

Python 基础知识点详细整理

1. Python 简介与环境搭建 简介&#xff1a;解释 Python 的特点&#xff0c;解释其跨平台性、解释性和面向对象特性。 安装与配置&#xff1a;如何安装 Python&#xff0c;搭建 Python 环境&#xff0c;使用 pip 安装库。 集成开发环境 (IDE)&#xff1a;如 PyCharm、VSCode、…

王者农药更新版

一、启动文件配置 二、GPIO使用 2.1基本步骤 1.配置GPIO&#xff0c;所以RCC开启APB2时钟 2.GPIO初始化&#xff08;结构体&#xff09; 3.给GPIO引脚设置高/低电平&#xff08;WriteBit&#xff09; 2.2Led循环点亮&#xff08;GPIO输出&#xff09; 1.RCC开启APB2时钟。…

【教程】57帧! Mac电脑流畅运行黑神话悟空

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、先安装CrossOver。网上有许多和谐版&#xff0c;可自行搜索。&#xff08;pd虚拟机里运行黑神话估计够呛的&#xff09; 2、运行CrossOver&#xf…

AJAX 1——axios体验、认识URL、常用请求方法、HTTP协议、错误处理、form-serialize插件

AJAX 1——axios体验、认识URL、常用请求方法、HTTP协议、错误处理、form-serialize插件 1.AJAX入门与体验axios 定义&#xff1a;浏览器与服务器进行数据通信的技术 体验axios库&#xff0c;与服务器通信 引入axios.js使用axios函数 <p class"my-p"></p&…