登录演示和功能拆解

表单基础校验实现
1. 基础双向绑定
<template>
<el-form>
<el-form-item label="账号">
<el-input v-model="formData.username" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="formData.password" />
</el-form-item>
<el-form-item>
<el-checkbox v-model="formData.remember">记住我</el-checkbox>
</el-form-item>
<el-form-item>
<el-button type="primary" class="login_btn">登录</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'Login',
data() {
return {
formData: {
username: '',
password: '',
remember: ''
}
}
}
}
</script>
2. 表单校验配置
- 按照业务要求编写校验规则对象(rules)
- el-form组件绑定表单对象(model)和规则对象(rules)
-
el-form-item组件通过prop属性指定要使用的校验规则
<template> <el-form :model="formData" :rules="rules"> <el-form-item label="账号" prop="username" > <el-input v-model="formData.username" /> </el-form-item></template> <script> export default { name: 'Login', data() { return { formData: { username: '', password: '', remember: '' }, rules: { username: [ { required: true, message: '请输入账号', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' } ] } } } } </script><el-form-item label="密码" prop="password" > <el-input v-model="formData.password" /> </el-form-item> <el-form-item prop="remember"> <el-checkbox v-model="formData.remember">记住我</el-checkbox> </el-form-item> <el-form-item> <el-button type="primary" class="login_btn">登录</el-button> </el-form-item> </el-form>
表单统一校验实现
业务背景:表单校验部分的触发条件是失焦事件,如果用户打开界面后直接点击登录按钮,校验将失效,所有需要在点击登录按钮时统一对所有表单进行校验
实现思路:通过调用form组件实例对象的validate方法
<template>
<el-form ref="form">
<el-form-item>
<el-button type="primary" class="login_btn" @click="doLogin()">登录</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'Login',
methods: {
doLogin() {
this.$refs.form.validate((valid) => {
if (valid) {
// TODO
console.log('登录')
}
})
}
}
}
</script>
Vuex管理用户Token
业务背景:由于用户数据的特殊性,可能需要在多个模块中进行使用,适合使用Vuex集中管理
开发模式:有关token的所有操作都放到Vuex中做,组件只做一个事儿就是触发action函数

实现步骤:
- 根据接口文档封装登录接口
- 在vuex中编写user模块的相关代码
- 组件中表单校验通过之后提交action
1. 封装登录接口
src/apis/user.js
import request from '@/utils/request'
// 登录函数
/**
* @description: 登录函数
* @param {*} data { mobile,password}
* @return {*} promise
*/
export function loginAPI({ username, password }) {
return request({
url: '/park/login',
method: 'POST',
data: {
username,
password
}
})
}
utils/request.js
const service = axios.create({
baseURL: 'https://api-hmzs.itheima.net/tj',
timeout: 5000 // request timeout
})
2. 编写Vuex相关代码
src/store/modules/user.js
import { loginAPI } from '@/api/user'
export default {
namespaced: true,
state: () => {
return {
token: ''
}
},
mutations: {
setToken(state, token) {
state.token = token
}
},
actions: {
async doLogin(ctx, { username, password }) {
// 1. 调用接口
const res = await loginAPI({ username, password })
// 2. 提交mutation
ctx.commit('setToken', res.data.token)
}
}
}
3. 组件中提交action
doLogin() {
this.$refs.form.validate(async(valid) => {
console.log(valid)
if (valid) {
// TODO
console.log('登录')
const { username, password } = this.formData
await this.$store.dispatch('user/doLogin', { username, password })
this.$router.push('/')
}
})
}

用户Token持久化
vuex-persistedstate 是一个 Vuex 插件,它允许你将 Vuex 的状态持久化存储在浏览器的 localStorage 或 sessionStorage 中。这对于需要在页面刷新后保持某些状态非常有用
vuex-persistedstate的工作机制:
- 在 mutation 完成对state的更新后,
vuex-persistedstate监听到状态的变化 vuex-persistedstate将更新后的状态序列化(通常是转换为 JSON 字符串)- 序列化后的数据被保存到指定的持久化存储中(例如
localStorage,sessionStorage中)
使用步骤:
-
npm install vuex-persistedstate 或者 yarn add vuex-persistedstate 安装
-
在store/index.js中使用
-
登录成功后,观察本地存储中如果有token即为成功
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)export default new Vuex.Store({
plugins: [
createPersistedState({
key: 'hm--vuex-token', // 存储在 localStorage 中的键名,默认为 'vuex'
storage: window.localStorage, // 或者 window.localStorage,默认为 localStorage
paths: ['user.token'] // 指定要持久化的 state 的路径,默认为所有 state
})
],
modules: {
user
}
})
Axios请求头中添加Token
- 为什么要添加Token到请求头?
点击查看答案接口需要做鉴权,只有token有效,才能返回正常数据,token就是后端用来做判断的标识
- 为什么要在axios中加?
点击查看答案项目中有很多接口都需要加鉴权功能,axios请求拦截器可以同一控制,一次添加,多个接口生效```
import store from '@/store'
// 添加请求拦截器
request.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
const token = store.state.user.token
if (token) {
config.headers.Authorization = token
}
return config
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error)
})
记住我功能实现

交互表现:
- 如果当前用户选中了checkbox,点击登录之后,再次回到登录,应该把之前输入的用户名和密码回填到输入框里面;
- 如果当前用户取消了checkbox,点击登录之后,再次回到登录,应该把之前存到本地的数据清除掉
实现思路:
- 完成选择框的双向绑定 得到一个true或者false的选中状态
- 如果当前为true,点击登录时,表示要记住,把当前的用户名和密码存入本地
- 组件初始化的时候,从本地取账号和密码,把账号密码存入用来双向绑定的form身上
-
如果当前用户没有记住,状态为false,点击登录的时候要把之前的数据清空
<script> const FORMDATA_KEY = 'form_key' export default { name: 'Login', mounted() { const cacheFormStr = localStorage.getItem(FORMDATA_KEY) if (cacheFormStr) { const cacheFormData = JSON.parse(cacheFormStr) this.formData.username = cacheFormData.username this.formData.password = cacheFormData.password } }, methods: { doLogin() { this.$refs.form.validate((valid) => { console.log(valid) if (valid) { // TODO console.log('登录') const { username, password, remember } = this.formData this.$store.dispatch('user/doLogin', { username, password })// 补充remeber逻辑 if (remember) { localStorage.setItem(FORMDATA_KEY, JSON.stringify({ username, password })) } else { localStorage.removeItem(FORMDATA_KEY) } } }) } }}
</script>
退出登录功能实现

询问用户是否真的要退出 -> 实现退出登录逻辑 ( 1. 清空当前用户的所有信息 2. 调回到登录页 )
1. 编写清除用户信息mutation
mutations: {
clearUserInfo(state) {
// 清除Token
state.token = ''
}
},
2. 提交mutation跳回到登录页
methods: {
// 退出登录
logout() {
this.$store.commit('user/clearUserInfo')
this.$router.push('/login')
}
}
Token控制路由跳转
业务背景:如果用户没有登录,不让用户进入到页面中,所以需要通过token的有无来控制路由的跳转

说明:白名单指的是不需要用户登录就可以看到的路由,比如/loigin
编写权限控制逻辑
// 导入vuex仓库对象
import store from '@/store'
// 路由守卫
router.beforeEach((to, from, next) => {
// 登录页面直接放行
if (to.path === '/login') {
next()
} else {
const token = store.state.user.token
// 判断是否有token
if (token) {
// 放行
next()
} else {
// 跳转回登录页
next('/login')
}
}
})
接口错误统一处理
背景:
- 接口报错的时候提示用户到底是哪里错误
-
接口数量很多 统一管控 不管哪个接口报错了 都能监控到 而且给出提示
import { Message } from 'element-ui'
// 响应拦截器
service.interceptors.response.use(
response => {
return response.data
},
error => {
// 错误统一处理
Message.error(error.response.data.msg)
return Promise.reject(error)
}
)export default service
Token失效处理
业务背景:Token存在一定的有效时间,如果长时间不进行接口访问,Token有可能就失效了,需要我们做统一控制
核心思路:因为我们不知道到底用户实在访问哪个接口的时候发生了Token失效访问,所以需要通过拦截器来做
- 跳转到登录页
-
清除掉过期Token
// 响应拦截器
service.interceptors.response.use(
response => {
return response.data
},
error => {
// Token 401处理
console.dir(error.response.status)
if (error.response.status === 401) {
// 1. 跳转到登录
router.push('/login')
// 2. 清空用户数据
store.commit('user/clearUserInfo')
}
return Promise.reject(error)
}
)