包括配置环境变量封装 axios、接口请求模块、vuex 登录请求动作、保存服务端的 token、登录鉴权

一、配置环境变量及封装 axios 模块

在跟目录下创建.env.development .env.production 2 个文件

1
2
3
4
5
6
7
// .env.development
ENV = "development";
VUE_APP_BASE_API = "/api";

// .env.production
ENV = "production";
VUE_APP_BASE_APIU = "/prod-api";

axios 的封装 utils 下创建 request.js

1
2
3
4
5
6
7
import axios from "axios";
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000,
});

export default service;

二、封装登录请求动作

根目录下的 src 下创建 api 文件夹,touch sys.js

1
2
3
4
5
6
7
8
import request from "@/utils/request";
export const login = (data) => {
return request({
url: "/sys/login",
method: "PSOT",
data,
});
};

将登录请求的动作封装到 vuex 的 action 中,在store文件夹下创建 modules 文件夹,在 modules 下touch user.js模块(此模块用于处理所有和用户相关的内容)

安装 md5 插件,加密登录密码 yarn add md5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 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,
password: md5(password)
})
.then(data => {
resolve()
})
.catch(err => {
reject(err)
})
})
}
}
}

// 在store/index.js中注册
import { createStore } from 'vuex'
import user from './modules/user.js'
export default createStore({
modules:{
user
}
})

在登录页,触发定义的 action,此时存在一个问题就是,我们当前请求的接口不存在,需要使用 devServer 代理

1
2
3
4
5
6
7
8
9
10
11
12
// vue.config.js
module.exports = {
devService: {
proxy: {
"/api": {
// 要代理的服务器地址, 不需要写api
target: "https://api.xxx.com",
changeOrigin: true, // 是否跨域
},
},
},
};

重启项目,再次点击登录,就可以跳转到首页了。

三、本地缓存的处理

登录成功之后,前端会拿到后端返回的 token,我们需要将 token 进行缓存

缓存的 2 种方式:

1、本地缓存:localStorage
2、全局状态管理: vuex

localStorage 可以方便实现自动登录功能
保存在 vuex 中是为了后面在其他位置进行使用

(一)、localStorage

在 utils 文件夹下,新建 storage..js
1、封装 4 个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 存储数据
*/
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();
};

(二)、vuex 处理 token

在 store 的 user.js 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { login } from '@/api/sys'
import md5 from 'md5'
import { setItem, getItem } from '@/utils/storage'
// 常量目录 export const TOKEN = 'token'
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()
})
...
})
}
}
}

四、响应数据的统一处理

上面这一行代码this.commit('user/setToken', data.data.data.token),需要使用 data.data.data.token,书写方式不美观。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

上面的代码简写为:

1
this.commit("user/setToken", data.token);