目录
一.样式调整
- 设置登录表单上的logo图
- 设置整体面页的背景图片
- 设置输入框中的字体颜色
- 设置登陆按钮的样式
- 修改显示的提示文本内容
- 设置输入表单整体背景色
- 设置错误信息的颜色(表单验证不通过时的提示信息)
知识点:
1.@
是我们在vue.config.js中设置的一个路径别名,指向src根目录,这样可以很方便的寻找文件(例如 " <img src="@/assets/common/login-logo.png" alt="">")
2. 如需要在样式表中<style></style>引入文件时,使用
@
别名,需要在@前面加上一个~
符号,~是相对于其他路径(文件)的,类似于相对路径,否则不识别(例如~@ "background-image: url('~@/assets/common/login.jpg'); // 设置背景图片")
疑点:
Vue中导入路径的@/和./的区别
./:表示相对路径,具体代表当前目录下的同级目录,遵从的是从后往前找文件
@/:表示的是相对路径,@ 代表就是 src 目录,遵从的是从前往后找
二.登录表单的数据项
el-form表单验证的格式
<el-form :model="" :modelRule="" :modelRef="" 绑定model 和 rules规则和
<el-form-item 绑定prop属性 pros=""
<el-input 绑定v-model :v-model=""
知识点
1.Form 组件提供了表单验证的功能,用来获取数据,只需要通过
rules
属性传入约定的验证规则,并将 Form-Item 的prop
属性设置为需校验的字段名即可,ref用于非空校验(this.$refs.xxx)2.简单的正则校验
用户名:/^[a-zA-Z0-9_-]{4,16}[$/ 4到16位(字母,数字,下划线,减号)
密码:/^\S6,16}$/ 6-16位非空字符串
手机号: /^1[3-9]\d{9}$/
email:/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
其他网上自查:https://blog.csdn.net/weixin_46787337/article/details/123508129
表单校验总结
- 完整的表单校验需要三个组件完成配合 分别是
el-form
,el-form-item
,表单项,如:el-input
el-form负责绑定model 和 rules
el-form-item负责绑定prop
el-input 负责双向绑定具体的表单数据 - 简单的校验例如非空,长度等可以直接使用内置的校验配置,如果校验规则复杂建议使用校验函数进行校验。
比如:我们的手机号比较复杂就使用更加灵活的校验函数
比如:密码比较简单我们直接使用内置的校验配置即可 - 手动兜底校验(this.$refs.xxx.validate((valid)=>{})
三.登录功能核心实现
步骤:
- 按接口文档要求写封装接口(src/api/user.js 中添加一个方法)
/**
* @description: 登录
* @param {*} data {modile, password}
* @return {*}
*/
export function login(data) {
return request({
url: 'api/sys/login',
method: 'post',
data
})
}
2.设置axios基地址(线上的)
const service = axios.create({
baseURL: '', // 设置axios请求的基础的基础地址
timeout: 5000 // 定义5秒超时
})
3.在组件中调用接口
导入在src/api/user.js封装的登录接口
通过表单验证(非空校验)后,执行登录动作
// 导入login方法
import { login } from '@/api/user'
async doLogin () {
//try ..catch 不影响代码的继续运行
try{
const res = await login(this.loginForm) //发送过去请求参数,调用接口函数
console.log('获取到的token是', res)
} catch (err) {
console.log('登录错误,原因是', err)
}
},
hLogin() {
// 先做校验
this.$refs.loginForm.validate( valid => {
if (valid) {
this.doLogin()
}
})
}
四.修改axios的相应拦截器
解决问题:
- 登录时,填写错误的用户名密码,没有错误提示(没有进入catch分支)
- 获取ajax的返回结果比较麻烦:res.data.data.XXXX
如下两种情况axios会主动抛出错误:
- 如果本次请求得到的响应的状态码 不是2开头的(如:400,500)
- 如果本次请求出现网络错误(如:断网,超时)
当我们提供错误的用户名和密码时,还是能从服务器获得到的数据,这个过程有如下两个特点:
- 本次网络请求的状态码是正常的(但是:success字段是false)
- 没有网络错误
所以axios并不会主动报错。如下图示
响应拦截器的基本代码:(相应拦截器会在数据处理返回时执行相关操作)
// 响应拦截器
service.interceptors.response.use(response => {
if (response.data.success) {
// 操作成功
} else {
// 如果success为false 业务出错,直接触发reject
// 被catch分支捕获
return Promise.reject(new Error('请求错误'))
}
}, error => {
return Promise.reject(error) // 返回执行错误 让当前的执行链跳出成功 直接进入 catch
})
观察api的返回结果,我们发现axios在处理接口返回值时,默认会自动给包裹一个data字段,这导致我们每次在业务模块获取数据都需要写
res.data.data.xxxx
。
解决思路:
在返回有效数据时,直接返回res.data
// 响应拦截器
service.interceptors.response.use(response => {
if (response.data.success) {
// 操作成功
+ return response.data
} else {
// 如果success为false 业务出错,直接触发reject
// 被catch分支捕获
return Promise.reject(new Error('请求错误'))
}
}, error => {
return Promise.reject(error) // 返回执行错误 让当前的执行链跳出成功 直接进入 catch
})
小结:
- 响应拦截器用来处理后端接口返回的数据---脱壳处理。
- 判断某个操作是否在业务是成功的,需要根据后端接口返回值中某个特殊字段(一般是由后端同学来约定)来判断,而不能只是依靠axios内置的报错机制
五.环境变量
环境变量在vue.config.js中配置
vue.config.js
就是vue项目相关的编译,配置,打包,启动服务相关的配置文件,它的核心在于webpack,相当于在webpack的基础上又做了一次抽象。官网地址: 地址
开发环境服务端口的在vue.config.js中 位置
环境变量的配置文件
文件名称 | 对应环境 | 说明 |
.env.development | 开发环境 | 当运行 的时候会以此文件为配置文件,这个文件中可以定义针对开发环境的环境变量 |
.env.production | 生产环境 | 当运行 的时候会以此文件为配置文件,这个文件中可以定义针对开发环境的环境变量 |
.env.staging | 模拟生产环境 | 可以理解为production环境的镜像, 尽最大可能来模拟产品线上的环境(硬件,网络拓扑结构,数据库数据) |
定义环境变量
在.env.development环境配置中设置自定义环境变量 port:8080
使用环境变量
const service = axios.create({
// baseURL: 'http://ihrm-java.itheima.net'
baseURL: process.env.VUE_APP_BASE_API
})
六.跨域
问题:跨域-原因及解决方案
跨域是什么
在A地址(发起请求的页面地址)向B地址(要请求的目标页面地址)发起请求时,
如果A地址和B地址在: 协议 域名 端口 不全相同,则说明请求是跨域的;
跨域请求是非常常见的现象!
跨域请求报错的原因
请求是跨域的,并不一定会报错。普通的图片请求,css文件请求是不会报错的
跨域请求出现错误的条件: 浏览器同源策略 && 请求是ajax类型
bugtu
vue-cli中集成的跨域解决方案-代理
思路:
在前端服务和后端接口服务之间 架设一个中间代理服务,它的地址保持和前端服务一致,那么:
- 代理服务和前端服务之间由于协议域名端口三者统一不存在跨域问题,可以直接发送请求
- 代理服务和后端服务之间由于并不经过浏览器没有同源策略的限制,可以直接发送请求
这样,我们就可以通过中间这台服务器做接口转发,在开发环境下解决跨域问题,看起来好像挺复杂,其实vue-cli已经为我们内置了该技术,我们只需要按照要求配置一下即可。
vue-cli解决跨域配置说明
在
vue.config.js
配置文件中,有一项是devServer,它就是我们下边要操作的主角。module.exports = { devServer: { // ... 省略 // 代理配置 proxy: { // 如果请求地址以/api打头,就出触发代理机制 // http://localhost:9588/api/login -> http://localhost:3000/api/login '/api': { target: 'http://localhost:3000' // 我们要代理的真实接口地址 } } } } }
小结
- vue-cli集成了跨域代理功能------ 只能用在开发阶段。
- vue.config.js文件中,在devServe下按指定格式配置了proxy,再重启项目即可。
七.token-处理-整体分析
项目背景:本项目中,后端接口中只有登录接口是不需要token的,其他接口都需要token才能访问
分析:登陆功能之后,从后端获取到了token值,在接下来的其他请求中,接口服务器都会去检查token值,这里我们提前把它保存到统一管理公共状态的 vuex中
token-处理-将token保存到vuex中
目标:使用vuex来保存登录接口获取到的token
整体思路如下
1.在 store/modules/user.js vuex公共仓库的state中初始化token状态
const state = {
token: null // 默认为null
}
2.mutation中提供修改state.token数据的函数setToken
// 修改状态
const mutations = {
// 设置token
setToken(state, newToken) {
state.token = token // 设置token
},
// 删除token
removeToken(state) {
state.token = null // 删除vuex的token
}
}
3.通过在index.vue中调用mutation函数,把需要修改的值返回赋值给state,token
async doLogin() {
try {
const res = await login(this.loginForm)
// console.log('用户登录ok', res)
console.log('用户登录,返回token', res.data)
// 1. 保存token到vuex
// 如何在组件调用带命名空间mutations
// this.$store.commit("命名空间/mutations名",载荷)
+ this.$store.commit('user/setToken', res.data)
} catch (err) {
alert('用户登录,失败')
console.log('用户登录,失败', err)
}
},
handleLogin() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.doLogin()
} else {
console.log('error submit!!')
return false
}
})
}
小结:
使用vuex的基本流程:
- 定义数据。 在state中定义。
- 定义mutations。用它来操作数据。
- 在组件中调用mutations。this.$store.commit('模块名/mutation的名字', 参数)
token-处理-axios请求拦截器统一注入
token作为我们其它请求的用户标识,我们使用请求拦截器做一下统一处理,让每一个请求的header中都拥有token
目标:将登陆获取到的token统一注入到接口的请求头中 。
思路:我们在上面的操作中已经将token保存到了vuex中,接下来,只需要在axios 的请求拦截器中取出token值再填充到header中即可。
// 不是在vue组件中,不能通过this.$store.state.user....来获取token
// 要导入
import store from '@/store'
service.interceptors.request.use(
config => {
const token = store.state.user.token
// 如果当前存有token,就加在请求头上
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
}
, error => {
return Promise.reject(error)
})
注:上面的authorization和bearer 是本项目的后端接口要求的写法。
token-处理-持久化
目标:
现在的token是保存在vuex中,而页面刷新之后,vuex的内容丢失了,将会导致接口访问异常。所以我们需要对token做持久化处理: 刷新页面之后token不丢失。
思路:
- 在对token进行初始化的时候先从本地取一下,优先使用本地取到的值
- 在设置token的时候除了在vuex中存一份,在本地也同步存一份
- 在删除token的时候除了把vuex中的删除掉,把本地的也一并删除
在
utils/auth.js
中,基础模板已经为我们提供了获取token
,设置token
,删除token
这三个方法,完全可以直接使用。只需要将存储的key放置成特定值即可
import Cookies from 'js-cookie' const TokenKey = 'vue_admin_template_token' export function getToken() { return Cookies.get(TokenKey) } export function setToken(token) { return Cookies.set(TokenKey, token) } export function removeToken() { return Cookies.remove(TokenKey) }
在vuex中引入方法,并调用
import { getToken, setToken, removeToken } from '@/utils/auth' export default { namespaced: true, // 公共数据 state: { // 本地取一下token token: getToken() || null }, mutations: { // 设置token setToken(state, newToken) { state.token = newToken // 本地存储token setToken(newToken) }, // 删除token removeToken(state) { state.token = null // 删除本地token removeToken() } }, actions: {} }
整个登录功能小结
登陆页,发请求到获得服务器响应的整个过程。
- 表单验证(login/index.vue)
- utils/validate.js ---> validMobile
- 调用 api/user.js中封装好的api
- 找到axios实例:utils/request.js中获取axios实例
- 设置基地址,从环境变量中拼接
- 收集用户的参数,传给上一步中的api。(页面上收集页面的数据项名与接口中一致)
- 经过请求拦截器,添加请求头(添加token, utils/request.js)
- 代理转发(vue.config.js)
- 线上接口/本地接口:
后端服务器要启动,mongoDB也要启动;后端收到请求,返回数据
- 经过响应拦截器的处理(utils/request.js)
- 判断当前操作是否成功,决定是否axios报错
- 简化获取有效数据的写法(脱壳)
- 得到api调用之后结果(login.vue)
- 保存token到vuex(store/modules/user.js)
- token做持久化(utils/auth.js)
在vuex中的action里发登陆请求
目标:在vuex的action中发异步请求的写法。
import { getToken, setToken, removeToken } from '@/utils/auth'
+ import { login } from '@/api/user'
export default {
// ... 省略其他
actions: {
// data表示接口参数
async userLogin(context, obj) {
console.log('action userLogin', context, obj)
// 调用ajax请求去做登陆
const res = await login(obj)
// 在actions中,如果要修改state,还是要调用mutaions
// context.commit('mutation名', 参数)
context.commit('setToken', res.data)
}
}
}
在组件中调用action
async doLogin() {
try {
// const res = await login(this.loginForm)
// // console.log('用户登录ok', res)
// console.log('用户登录,返回token', res.data)
// // 1. 保存token到vuex
// // 如何在组件调用带命名空间mutations
// // this.$store.commit("命名空间/mutations名",载荷)
// this.$store.commit('user/setToken', res.data)
// 在组件中调用带命名空间的action
await this.$store.dispatch('user/userLogin', this.loginForm)
console.log('用户登录成功')
} catch (err) {
alert('用户登录,失败')
console.log('用户登录,失败', err)
}
},
总结:在组件中调用action,在action内部去ajax请求,再去调用commit调mutation来设置state
经验
- 应用共享的一些状态数据,我们可以都放到vuex里管理,包括异步请求发送,这样更加方便维护。
- this.$store.dispatch()它本身也是异步的,如果希望实现同步的效果,前边要加await