目录

目录

登录注册

准备

API文档

创建组件并配置路由

实现基本登录功能

登录状态提示

表单验证

验证码处理

发送验证码前先)验证手机号

使用倒计时组件

添加发送按钮的loading

存储用户Token

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

JSON和JS对象对比:

JSON和JS对象互转

关于Token过期问题(后期讲解)

登录注册

目标:

  • 能实现登录页面的布局
  • 能实现基本登录功能
  • 能掌握vant中Toast提示组件的使用
  • 能理解API请求模块的封装
  • 能理解发送验证码的实现思路
  • 能了解在vue中处理表单验证的方式

准备

        API文档

     

        创建组件并配置路由

        页面结构

<template>
  <div class="login-container">
    登录页面
  </div>
</template>
<script>
export default {
  name: "LoginIndex",
  data() {
    return {};
  }
};
</script>
<style></style>

        路由配置

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

// 路由表
const routes = [
  {
    path: '/login',
    name: '/login',
    // 路由懒加载形式
    // 完整写法:
    // component: () => import('@/views/login/index.vue')
    component: () => import('@/views/login')
  }
]
export default new Router({
  routes
})

实现基本登录功能

接口配置

1.接口:http://ttapi.research.itcast.cn/app/v1_0/authorizations

2.POST方式

3.接口需要传的参数:

{

    "mobile": "13911111111",

    "code": "246810"

}

登录联调接口

1.配置登录模块接口 api/user.js

java获取今日头条文章链接 今日头条接口文档_html

2. 登录组件请求接口

  • 引入模块  import { login } from "@/api/user"; 
  •  点击登录提交方法  await login(user);
async onSubmit() {
      // 1. 获取表单数据
      const user = this.user;
      // 2.表单验证
      this.$toast.loading({
        message: "登录中..",
        forbidClick: true, // 禁用背景点击
        duration: 2000 // 持续时间,默认2000,若为0则持续展示
      });

      // 3. 提交表单请求登录
      try {
        const res = await login(user);
        // console.log("登录成功", res);
        this.$toast.success("登录成功");
      } catch (err) {
        if (err.response.status === 400) {
          console.log("手机号或验证码错误");
          this.$toast.fail("手机号或验证码错误");
        } else {
          console.log("登录失败,请稍后重试", err);
          this.$toast.fail("登录失败,请稍后重试");
        }
      }

      // 4.根据请求响应结果处理后续操作
    }

登录状态提示

1.加载:

this.$toast.loading({
        message: "登录中..",
        forbidClick: true, // 禁用背景点击
        duration: 2000 // 持续时间,默认2000,若为0则持续展示
      });

2.成功:this.$toast.success("登录成功");  失败:this.$toast.fail("手机号或验证码错误");

表单验证

基于vant的表单验证:

1.使用van-form组件包裹所有的表单项 van-field,包括登录按钮都包含在van-form组件

<van-form @submit="onLogin"></van-form>

2.给van-form绑定onLogin事件,当表单提交时触发 onLogin

(只有表单验证通过才会调用onLogin事件)

注:在此之前onLogin事件的名字为onSubmit,这里方便识别改成onLogin

3.使用Field的的rules属性定义校验规则

法一(用于验证规则较少)

:rules="[{ required: true, message: '请输入手机号' }]"

法二(用于验证规则较多)

首先 :rules="formRules.mobile"

然后 formRules: {

        mobile: [

          { required: true, message: "请输入正确的手机号" },

          { pattern: /^1[3|5|7|8]\d{9}$/, message: "手机号格式错误" }

        ],

        code: [

          { required: "true", message: "请输入验证码" },

          { pattern: /^\d{6}$/, message: "验证码格式错误" }

        ]

      }

自定义错误消息提示

1.van-form涉及的三个属性show-error,show-error-message,validate-first

2.绑定表单验证失败触发 @failed="onFailed"

3.onFailed方法

onFailed(error) {

      if (error.errors[0]) {

        this.$toast({

          message: error.errors[0].message,

          position: "top"

        });

      }

    }

验证码处理

        (发送验证码前先)验证手机号

1.基本流程

// 1.发送验证码前需要先检验手机号

// 验证通过后->请求发送验证码->用户接收短信->输入验证码->请求登录 

// 2.请求发送验证码->隐藏发送按钮,显示倒计时

// 3.倒计时结束->隐藏倒计时,显示发送按钮

2.使用van-form的validate属性,需要先ref绑定实例,因为validate的返回值是一个Promise,所以要用到.then()方法去接收:

this.$refs['login-form'].validate('mobile').then(data => {

        console.log(data)

})

这里我们为了方便起见,直接使用async await 方法

try{

        await this.$refs['login-form'].validate('mobile')

        // 验证通过,请求发送验证码

      }

      // 验证失败

      catch(err){

        this.$toast({

          message: err.message,

          position: 'top'

        })

      }

接口配置

1.接口:http://ttapi.research.itcast.cn/mp/v1_0/sms/codes/:mobile

2.GET方式

3.接口需要传的参数:

{

    "mobile": "13911111111",

    "code": "246810"

}

验证码联调接口

1.配置api/user.js 验证码

// 发送短信验证码
export const sendSms = mobile => { // 参数mobile
  return request({
    method: 'GET',
    url: `/app/v1_0/sms/codes/${mobile}`
  })
}

2.该组件页请求接口

  • 引入模块 import { login, sendSms } from "@/api/user";
  • 点击发送验证码的方法  await sendSms(this.user.mobile);
// 发送验证码
    async onSendSms() {
      // 1.检验手机号
      // 验证通过->请求发送验证码->用户接收短信->输入验证码->请求登录
      // 2.请求发送验证码->隐藏发送按钮,显示倒计时
      // 3.倒计时结束->隐藏倒计时,显示发送按钮
      // this.$refs['login-form'].validate('mobile').then(data => {
      //   console.log(data)
      // })
      try {
        await this.$refs["login-form"].validate("mobile");
        // 验证通过,请求发送验证码
        await sendSms(this.user.mobile);
      } catch (err) {
        // try 里面任何代码的错误都会进入catch
        let message = "";
        if (err && err.response && err.response.status === 429) {
          // 发送短信失败的错误提示
          message = "发送太频繁了,请稍后重试";
        } else if (err.name === "mobile") {
          // 发送验证失败的错误提示
          message = err.message;
        } else {
          // 未知错误
          message = "发送失败,请稍后重试";
        }

        this.$toast({
          message: message,
          position: "top"
        });
      }
    }

        使用倒计时组件

1.使用countDown倒计时组件

// 倒计时显示v-if

<van-count-down

    v-if="isCounDownShow"

    :time="1000 * 60"

     format="ss s"

/>

// 否则发送验证码按钮显示 v-else

2.初始化isCounDownShow: false,用来控制倒计时和发送按钮的显示状态

3.添加倒计时结束的方法,隐藏倒计时 @finish="isCounDownShow = false"

        添加发送按钮的loading

用户点击完”发送验证码“进行网络请求时,页面并没有立即显示”倒计时“,这会导致用户以为并未点击上”发送验证码“按钮,通过控制台可以看出实际上已经点击了,防止网络过慢,用户多次触发发送行为

java获取今日头条文章链接 今日头条接口文档_验证码_02

 解决:

1.点击完后可以将按钮设置为禁用

2.点击完后可以将按钮设置为loading属性,表示正在加载 

存储用户Token

用户只有在第一次登录成功之后才能拿到Token,为了后续在其它模块中能获取到Token数据,需要存储到一个公共位置

  • 本地存储,不推荐,获取麻烦,数据不是响应式
  • Vuex容器,推荐,获取方便,响应式

 1.创建src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // user: null,
    // 还原为对象
    user: JSON.parse(window.localStorage.getItem('user')) // 当前登录用户的登录状态(token等数据)
  },
  // 修改state对象的值
  mutations: {
    // 参数:state对象,data所传入的参数
    setUser (state, data) {
      state.user = data

      // 容器中的数据不是持久化的,刷新后就不存在了
      // 为了防止页面刷新数据丢失,还需要把数据存储到本地存储中,这里仅仅是为了持久化数据
      // 对象state.user不能直接存入本地存储中,要转换为字符串
      window.localStorage.setItem('user', JSON.stringify(state.user))
    }
  }
})

重点:

容器中的数据不是持久化的,刷新后就不存在了
为了防止页面刷新数据丢失,还需要把数据存储到本地存储中,这里仅仅是为了持久化数据
对象state.user不能直接存入本地存储中,要转换为JSON字符串 JSON.stringify()

2.在登录组件login/index.vue发起网络请求时并登录成功,将后端返回的用户登录状态存入Vuex容器中

// 将后端返回的用户登录状态(token等数据)放到Vuex容器中

this.$store.commit("setUser", res.data.data);

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

1.创建src/utils/storage.js文件

// 本地存储封装模块
export const getItem = name => {
  const data = window.localStorage.getItem(name)
  // 为什么把JSON.parse放到try-catch中
  // 因为data可能不是JSON格式字符串
  try {
    // 尝试把data转为JavaScript对象
    return JSON.parse(data)
  } catch (err) {
    // data不是JSON格式字符串,直接原样返回
    return data
  }
}

export const setItem = (name, value) => {
  // 如果value是对象,就把value转为JSON格式字符串再存储
  if (typeof value === 'object') {
    value = JSON.stringify(value)
  }
  window.localStorage.setItem(value)
}

export const removeItem = name => {
  window.localStorage.removeItem(name)
}

2.引入文件 import { getItem, setItem } from '@/utils/storage'

import Vue from 'vue'
import Vuex from 'vuex'
import { getItem, setItem } from '@/utils/storage'
Vue.use(Vuex)

const USER_KEY = 'user'

export default new Vuex.Store({
  state: {
    // user: null,
    // 还原为对象
    // user: JSON.parse(window.localStorage.getItem('user')) // 当前登录用户的登录状态(token等数据)
    // 优化为:
    user: getItem(USER_KEY)
  },
  // 修改state对象的值
  mutations: {
    // 参数:state对象,data所传入的参数
    setUser (state, data) {
      state.user = data

      // 容器中的数据不是持久化的,刷新后就不存在了
      // 为了防止页面刷新数据丢失,还需要把数据存储到本地存储中,这里仅仅是为了持久化数据
      // 对象state.user不能直接存入本地存储中,要转换为JSON字符串
      // window.localStorage.setItem('user', JSON.stringify(state.user))
      // 优化为:
      setItem(USER_KEY, state.user)
    }
  }
})

补充:

 一直以为JSON是对象,然而网上查阅才发现JSON与JS有很大的不同。 
  var obj={width:100,height:200},这样的并不叫JSON,并且JSON只是一种数据格式,并不是具体的实例对象。但很多人把这样的JS对象当成JSON,JSON是不能用来存诸数据的

  1. var obj5={"width":100,"height":200,"name":"rose"}; /*我们可以把这个称做:JSON格式的JavaScript对象 */
  2. var str1='{"width":100,"height":200,"name":"rose"}';/*我们可以把这个称做:JSON格式的字符串 */
  3. {"province": "Shanxi"} 可以理解为是一个包含province为Shanxi的对象,
    ["Shanxi","Shandong"]这是一个包含两个元素的数组

JSON和JS对象对比:

 

java获取今日头条文章链接 今日头条接口文档_验证码_03

 JSON和JS对象互转

java获取今日头条文章链接 今日头条接口文档_html_04

 关于Token过期问题(后期讲解)

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

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

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

        过期时间短是为了安全,防止token被盗用

        过期怎么办?

1.让用户重新登录,用户体验太差

        2.使用refresh_token解决token过期