黑马头条vue2.0项目实战(二)——登录注册功能的实现

avatar
作者
猴君
阅读量:0

1. 布局结构

目标

  • 能实现登录页面的布局

  • 能实现基本登录功能

  • 能掌握 Vant 中 Toast 提示组件的使用

  • 能理解 API 请求模块的封装

  • 能理解发送验证码的实现思路

  • 能理解 Vant Form 实现表单验证的使用

这里主要使用到三个 Vant 组件:

1.1 布局样式

        写样式的原则:将公共样式写到全局(src/styles/index.less),将局部样式写到组件内部。对于需要更改样式,自己添加 class 类名。

1.2 自定义图标的使用

官方文档实现:

实现效果:

最左侧显示的图标无法满足需求的情况下,可以通过 slot 实现,插入自定义的图标。可以插入的slot如下所示:

具体实现,通过插入 i 标签,设置 iconfont 类名。

1.2 插入按钮

官方文档:

实现效果:

        但是在表单中,除了提交按钮外,可能还有一些其他的功能性按钮,如发送验证码按钮。在使用这些按钮时,要注意将 native-type设置为button,否则会触发表单提交。

2. 实现基本登录功能

功能需求:

  • 注册点击登录的事件

  • 获取表单数据(根据接口要求使用 v-model 绑定)

  • 表单验证

  • 发请求提交

  • 根据请求结果做下一步处理

2.1 登录状态提示

2.2 表单验证

  •  给 vant-field 组件配置 rules 验证规则     

  • 可以在 data 里自定义校验规则校验手机号和验证码

  • 当给表单提交的时候会自动触发表单验证
    • 如果验证通过,则触发 submit 事件
    • 如果验证不通过,则不会触发 submit 事件
    • 验证规则 参考vant组件的文档

2.3 验证码处理

点击发送验证码功能

功能需求:

  • 点击发送验证码按钮后,对用户输入的手机号进行验证

  • 验证通过显示倒计时

  • 发送用户手机号给后端,后台给用户手机下发验证码
  • 发送失败关闭倒计时

注意:

  • 这里的 ref 是绑在表单 Form上
  • 获取表单实例之后,通过 .validate('校验表单或输入框的name属性值')
  • 返回的是一个 promise 对象

3. 处理用户 Token

Token 是用户登录成功之后服务端返回的一个身份令牌,在项目中的多个业务中需要使用到:

  • 访问需要授权的 API 接口

  • 校验页面的访问权限

  • ...

但是我们只有在第一次用户登录成功之后才能拿到 Token。

所以为了能在其它模块中获取到 Token 数据,我们需要把它存储到一个公共的位置,方便随时取用。

往哪儿存?

  • 本地存储

    • 获取麻烦

    • 数据不是响应式

  • Vuex 容器(推荐)

    • 获取方便

    • 响应式的

3.1 使用容器存储 Token 的思路

  • 登录成功,将 Token 存储到 Vuex 容器中

    • 获取方便

    • 响应式

  • 为了持久化,还需要把 Token 放到本地存储

    • 持久化

登录成功以后将后端返回的 token 相关数据存储到容器中

3.2 优化封装本地存储操作模块

创建 src/utils/storage.js 模块

3.3 关于 Token 过期问题

登录成功之后后端会返回两个 Token:

  • token:访问令牌,有效期2小时

  • refresh_token:刷新令牌,有效期14天,用于访问令牌过期之后重新获取新的访问令牌

我们的项目接口中设定的 Token 有效期是 2 小时,超过有效期服务端会返回 401 表示 Token 无效或过期了。

为什么过期时间这么短?

  • 为了安全,例如 Token 被别人盗用

过期了怎么办?

  • 让用户重新登录,用户体验太差了

  • 使用 refresh_token 解决 token 过期

后续拓展Token 过期处理 ,在学习测试的时候如果收到 401 响应码,请重新登录再测试

概述:服务器生成token的过程中,会有两个时间,一个是token失效时间,一个是token刷新时间,刷新时间肯定比失效时间长,当用户的 token 过期时,你可以拿着过期的token去换取新的token,来保持用户的登陆状态,当然你这个过期token的过期时间必须在刷新时间之内,如果超出了刷新时间,那么返回的依旧是 401。

处理流程:

  1. 在axios的拦截器中加入token刷新逻辑

  2. 当用户token过期时,去向服务器请求新的 token

  3. 把旧的token替换为新的token

  4. 然后继续用户当前的请求

在请求的响应拦截器中统一处理 token 过期(后续拓展)。

/**  * 封装 axios 请求模块  */ import axios from "axios"; import jsonBig from "json-bigint"; import store from "@/store"; import router from "@/router";  // axios.create 方法:复制一个 axios const request = axios.create({   baseURL: "http://ttapi.research.itcast.cn/" // 基础路径 });  /**  * 配置处理后端返回数据中超出 js 安全整数范围问题  */ request.defaults.transformResponse = [   function(data) {     try {       return jsonBig.parse(data);     } catch (err) {       return {};     }   } ];  // 请求拦截器 request.interceptors.request.use(   function(config) {     const user = store.state.user;     if (user) {       config.headers.Authorization = `Bearer ${user.token}`;     }     // Do something before request is sent     return config;   },   function(error) {     // Do something with request error     return Promise.reject(error);   } );  // 响应拦截器 request.interceptors.response.use(   // 响应成功进入第1个函数   // 该函数的参数是响应对象   function(response) {     // Any status code that lie within the range of 2xx cause this function to trigger     // Do something with response data     return response;   },   // 响应失败进入第2个函数,该函数的参数是错误对象   async function(error) {     // Any status codes that falls outside the range of 2xx cause this function to trigger     // Do something with response error     // 如果响应码是 401 ,则请求获取新的 token      // 响应拦截器中的 error 就是那个响应的错误对象     console.dir(error);     if (error.response && error.response.status === 401) {       // 校验是否有 refresh_token       const user = store.state.user;        if (!user || !user.refresh_token) {         router.push("/login");          // 代码不要往后执行了         return;       }        // 如果有refresh_token,则请求获取新的 token       try {         const res = await axios({           method: "PUT",           url: "http://ttapi.research.itcast.cn/app/v1_0/authorizations",           headers: {             Authorization: `Bearer ${user.refresh_token}`           }         });          // 如果获取成功,则把新的 token 更新到容器中         console.log("刷新 token  成功", res);         store.commit("setUser", {           token: res.data.data.token, // 最新获取的可用 token           refresh_token: user.refresh_token // 还是原来的 refresh_token         });          // 把之前失败的用户请求继续发出去         // config 是一个对象,其中包含本次失败请求相关的那些配置信息,例如 url、method 都有         // return 把 request 的请求结果继续返回给发请求的具体位置         return request(error.config);       } catch (err) {         // 如果获取失败,直接跳转 登录页         console.log("请求刷新 token 失败", err);         router.push("/login");       }     }      return Promise.reject(error);   } );  export default request;

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!