需求:根据当前环境的不同,请求不同的 BaseUrl
解决:在根目录中新建.env.development与.env.production连个文件,进行配置:
# .env.production
ENV = 'production'
# base api
VUE_APP_BASE_API = '/prod-api'
# .env.development
ENV = 'development'
# base api
VUE_APP_BASE_API = '/api'
然后在utils/request.js中配置:
import axios from 'axios'
const service = axios.create({
//process.env 表示当前环境下的所有环境变量
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
export default service
问题:
出现这个问题的原因,是因为我们在前面配置环境变量时指定了 开发环境下,请求的 BaseUrl
为 /api;
这样的一个请求会被自动键入到当前前端所在的服务中,所以我们最终就得到了 http://192.168.18.42:8081/api/sys/login
这样的一个请求路径
解决:通过指定 webpack DevServer 代理 的形式,代理当前的 url
请求;在vue.config.js中进行如下编辑
module.exports = {
devServer: {
// 配置反向代理
proxy: {
// 当地址中有/api的时候会触发代理机制
'/api': {
// 要代理的服务器地址 这里不用写 api
target: 'https://api.xxxxxxx.club/',
changeOrigin: true // 是否跨域
}
}
},
xxxxxxxxx
}
在vuex中进行请求处理
创建 api
文件夹,创建 sys.js
,配置请求:
import request from '@/utils/request'
/**
* 登录
*/
export const login = data => {
return request({
url: '/sys/login',
method: 'POST',
data
})
}
将封装登录请求处理封装到 vuex
的 action
中。在 store
下创建 modules
文件夹,创建 user.js
模块,进行请求处理:
import { login } from '@/api/sys'
import md5 from 'md5'
export default {
namespaced: true,
state: () => ({}),
mutations: {},
actions: {
login(context, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({
username,
// md5用来将密码加密处理
password: md5(password)
})
.then(data => {
resolve()
})
.catch(err => {
reject(err)
})
})
}
}
}
在 store/index
中完成注册:
import { createStore } from 'vuex'
import user from './modules/user.js'
export default createStore({
modules: {
user
}
})
在 login
中,触发定义的 action
:
<template>xxxxxx</template>
<script setup>
import { useStore } from "vuex";
const store = useStore();
const handleLogin = () => {
//先校验数据,校验省略,数据格式校验通过进行登录
store
.dispatch("user/login", loginForm.value)
.then(() => {
// TODO: 登录后操作
})
.catch((err) => {
console.log(err);
});
};
</script>
本地缓存处理方案
创建 utils/storage.js
文件,封装对本地LocalStorage的操作:注意两种不同数据类型的处理
/**
* 存储数据
*/
export const setItem = (key, value) => {
// 将数组、对象类型的数据转化为 JSON 字符串进行存储
if (typeof value === 'object') {
value = JSON.stringify(value)
}
window.localStorage.setItem(key, value)
}
/**
* 获取数据
*/
export const getItem = key => {
const data = window.localStorage.getItem(key)
try {
return JSON.parse(data)
} catch (err) {
return data
}
}
/**
* 删除数据
*/
export const removeItem = key => {
window.localStorage.removeItem(key)
}
/**
* 删除所有数据
*/
export const removeAllItem = key => {
window.localStorage.clear()
}
创建 constant
常量目录 constant/index.js
:
export const TOKEN = 'token'
在 vuex
的 user
模块下,处理 token
的保存:
import { login } from '@/api/sys'
import md5 from 'md5'
import { setItem, getItem } from '@/utils/storage'
import { TOKEN } from '@/constant'
export default {
namespaced: true,
state: () => ({
token: getItem(TOKEN) || ''
}),
mutations: {
setToken(state, token) {
state.token = token
setItem(TOKEN, token)
}
},
actions: {
login(context, userInfo) {
...
.then(data => {
this.commit('user/setToken', data.data.data.token)
resolve()
})
...
})
}
}
}
通过axios响应拦截器处理data.data.data.token问题。在 utils/request.js
中编码:
先放上响应的数据结果:
import axios from 'axios'
import { ElMessage } from 'element-plus'
...
// 响应拦截器
service.interceptors.response.use(
response => {
const { success, message, data } = response.data
// 要根据success的成功与否决定下面的操作
if (success) {
return data
} else {
// 业务错误
ElMessage.error(message) // 提示错误消息
return Promise.reject(new Error(message))
}
},
error => {
// TODO: 将来处理 token 超时问题
ElMessage.error(error.message) // 提示错误信息
return Promise.reject(error)
}
)
export default service
再修改下vuex 中的 user 模块
:
this.commit('user/setToken', data.token)
登录鉴权
当用户未登陆时,不允许进入除 login
之外的其他页面。用户登录后,token
未过期之前,不允许进入 login
页面
根目录创建permission.js,并在main.js中导入:
import router from './router'
import store from './store'
// 白名单
const whiteList = ['/login']
/**
* 路由前置守卫
*/
router.beforeEach(async (to, from, next) => {
// 存在 token ,进入主页
// if (store.state.user.token) {
// 快捷访问
if (store.getters.token) {
if (to.path === '/login') {
next('/')
} else {
next()
}
} else {
// 没有token的情况下,可以进入白名单
if (whiteList.indexOf(to.path) > -1) {
next()
} else {
next('/login')
}
}
})
在此处的 getters
被当作 快捷访问 的形式进行访问,所以我们需要声明对应的模块,创建 store/getters
const getters = {
token: state => state.user.token
}
export default getters
在 store/index
中进行导入:
import getters from './getters'
export default createStore({
getters,
...
})