目录
- Axios封装、调用mock接口、Vite跨域及环境变量配置
- 封装Axios对象调用mock接口数据
- 第一步、安装axios,处理一部请求
- 第二步、创建`request.ts`文件
- 第三步、本地模拟mock数据接口
- 第四步、测试axios+mock接口是否可以调用
- 第五步、自行扩展 axios 返回的数据类型 axios.d.ts
- 跨域解决
- 什么是跨域
- 跨域示例
- 跨域解决
- 跨域实操
- Vite配置环境变量及模式
- Vite 配置环境变量
- Vite配置环境模式
- 重构代理配置
- 完结~
Axios封装、调用mock接口、Vite跨域及环境变量配置
封装Axios对象调用mock接口数据
因为项目中有很多接口要通过Axios
发送异步请求,所以需要封装一个axios
对象,自己封装的Axios
后面可以时候axios
中提供的拦截器,参考官方文档
第一步、安装axios,处理一部请求
npm install axios
第二步、创建request.ts
文件
在 src
目录下创建utils
目录,然后 utils
目录下创建 request.ts
文件
import axios from 'axios';
import type { AxiosInstance } from 'axios';
import { ElMessage } from 'element-plus';
// 手动创建一个 axios 对象, 参考: https://github.com/axios/axios#creating-an-instance
const request: AxiosInstance = axios.create({
//baseURL: 'https://mock.xxx.com/mock/64fa8039e70b8004a69ea036/hsk-admin',
// 根据不同环境设置 baseURL, 最终发送请求时的URL为: baseURL + 发送请求时写URL ,
// 比如: `baseURL: '/dev-api'` ,当请求 get('/test'), 最终发送请求是: /dev-api/test
// baseURL: '/dev-api',
// 获取项目根目录下 .env.xxxx 文件中的环境变量值
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 20000, // 请求超时的毫秒数,请求时间超过指定值,则请求会被中断
});
// 请求拦截器
request.interceptors.request.use(config => {
// 在此处可向请求头加上认证token
return config;
}, error => {
// 出现异常, catch可捕获到
return Promise.reject(error);
})
// 响应拦截器
request.interceptors.response.use(response => {
// console.log('响应拦截器', response);
const res = response.data;
// 20000 正常响应,返回响应结果给调用方
if (res.code === 20000) {
return res;
}
// 非正常响应弹出错误信息,
ElMessage.error(res.message);
return Promise.reject(res);
}, error => {
// 处理响应错误
const { message, response } = error;
if (message.indexOf('timeout') != -1) {
ElMessage.error('网络超时!');
} else if (message == 'Network Error') {
ElMessage.error('网络连接错误!');
} else {
if (response.data) ElMessage.error(response.statusText);
else ElMessage.error('接口路径找不到');
}
return Promise.reject(error);
});
export default request; // 导出 axios 对象
第三步、本地模拟mock数据接口
前后端分离开发过程中,后端数据接口还没有写出来,前端可以使用mock
模拟假数据进行先一步页面的开发,使用mockjs
模拟后端接口,可随机生成所需要的数据,模拟对数据的增删查改,mock
支持丰富的数据类型,支持生成随机的文本
,数字
,布尔值
,日期
,邮箱
,链接
,图片
,颜色
等,拦截Ajax
请求不需要修改既有代码可以拦截,返回模拟的响应数据。
项目中使用mockjs
的时候,首先确保安装了axios
和mock
,上面第一步的时候已经安装了axios
数据,现在开始进行安装mock
:
npm install -D mockjs
在项目的src
文件夹下新建一个文件夹用来存放mock
模拟的数据,一般我们放在将mock
模拟的数据( /src/mock/index.js
)这个文件中,这里以此为例。
//这里是我使用本地的服务器商品接口地址模拟的数据
import { mock } from 'mockjs'
let data = mock({
"code": 20000,
"message": "查询成功",
"data": [
{ "name": "小梦", "age": 18 },
{ "name": "涛姐", "age": 32 },
{ "name": "林志玲", "age": 48 }
]
})
mock(/test/, 'get', () => {
return data
})
模拟完数据后,在入口主文件 main.js
中引入这个模拟数据的文件
import "./mock/index.js"
第四步、测试axios+mock接口是否可以调用
在 src/
下创建 /api/test.ts
目录和文件, 调用接口代码如下:
import request from "@/utils/request";
export function test1() {
// 测试1: 调用 get 方式发送get请求
request.get("/test").then(response => {
console.log("get1", response);
}).catch(error => {
console.log('error', error);
});
}
export function test2() {
// 测试2, 使用对象形式传入请求配置,如 请求url, method,param
request({
url: `/test`,
method: "GET"
}).then(response => {
console.log("get2", response);
}).catch(error => {
console.log(error);
});
}
app.vue
页面引入接口并调用:
<template>
<div>
<el-icon><ele-Search /></el-icon>
<SvgIcon name="ele-Search"></SvgIcon>
<el-button type="primary">Primary</el-button>
</div>
</template>
<script lang="ts" setup>
// 导入 test.ts,
import { test1, test2 } from "../src/api/test.ts";
// 调用方法发送请求
test1();
test2();
</script>
<style lang="scss">
// 编写 scss 代码
</style>
效果:
测试三、通过 api
方法返回请求的 Promise
对象,然后在调用方通过then
获取响应数据,api\test.ts
新增如下代码:
import request from "@/utils/request";
// 返回 Promise
export function getList() {
const req = request({
url: `/test`,
method: "GET"
});
// console.log(req) // Promise
return req;
}
在 App.vue
中导入 test.ts
<script setup lang='ts'>
// 导入 test.ts,调用方法发送请求
import { getList } from "@/api/test";
import { onMounted } from "vue";
onMounted(() => {
loadData();
loadData2();
});
function loadData() {
getList()
.then((response: any) => {
console.log("loadData", response);
})
.catch((error: Error) => {
console.log(error);
});
}
// 使用 async+await
async function loadData2() {
const response = await getList();
console.log("loadData2", response);
}
</script>
后面数据访问都采用测试三这种方式。
第五步、自行扩展 axios 返回的数据类型 axios.d.ts
-
使用
axios
发送请求接口后,axios
生命的响应对象AxiosResponse
中的属性并不是我们想要的,我们需要自行扩展为我们需要相应的数据类型。/* eslint-disable */ import * as axios from 'axios'; // 自行扩展 axios 返回的数据类型 declare module 'axios' { export interface AxiosResponse<T = any> { code: number; message: string; data: T; } }
declare
定义的接口类型,在SFC
和ts
文件中不需要导入,相当于全局接口,直接引用接口类型。但是要告知ts
文件在哪里 -
自动加载
*.d.ts
文件,修改tsconfig.app.json
(旧版本在tsconfig.json
中)文件的includes
选项值:追加"src/**/*.d.ts"
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "src/**/*.d.ts"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
注意:添加后,重启编辑器VsCode
,不然可能无法识别到*.d.ts
文件
跨域解决
什么是跨域
前后端分离时,前端和后端API服务器可能不在同一台主机上面,就存在跨域问题,浏览器限制了跨域访问,违反了同源策略【协议,域名,端口】都要相同,其中一个不同都会产生跨域。
跨域示例
- 前端部署在
http://127.0.0.1:8888/
即本地ip
端口8888
上面- 后端API部署在
http://127.0.0.1:9999/
即本地ip
端口999上面
他们的IP
一样,但是端口不一样就会存在跨域问题。
跨域解决
【跨域解决参考文档入口】
- 通过代理请求的方式解决,将
API
请求通过代理服务器请求到API
服务器。- 开发环境中,在
vite.config.ts
文件中使用server.proxy
选项进行代理配置。- 生产环境,可采用
nginx
代理 解决跨域问题。
跨域实操
- 在
vite.config.ts
文件中使用server.proxy
选项进行代理配置。
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
// 开发服务器选项,参考:https://cn.vitejs.dev/config/server-options.html#server-host
server: {
open: true, //启动服务时自动打开浏览器访问
port: 8888, //端口号, 如果端口号被占用,会自动提升1
proxy: { // ++++++++++++++++++ 解决跨域问题
'/dev-api': { // 匹配 /dev-api 开头的请求,
// 目标服务器, 代理访问到https://mock.mengxuegu.com/mock/6621e5cbfaa39b4567596484/adminPower
target: 'https://mock.mengxuegu.com/mock/6621e5cbfaa39b4567596484/adminPower',
// 开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,
// 这样服务端和服务端进行数据的交互就不会有跨域问题
changeOrigin: true,
// 将 /dev-api 替换为 '',也就是 /dev-api 会移除,
// 如 /dev-api/test 代理到https://mock.mengxuegu.com/mock/6621e5cbfaa39b4567596484/adminPower
rewrite: (path) => path.replace(/^\/dev-api/, ''),
},
}
},
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
- 将
src\utils\request.ts
中的baseURL
修改:const request: AxiosInstance = axios.create({ baseURL: '/dev-api', timeout: 20000, // 请求超时的毫秒数,请求时间超过指定值,则请求会被中断 });
- 测试:访问
http://127.0.0.1:8888/
,查看控制台响应了数据,说明代理成功
Vite配置环境变量及模式
Vite 配置环境变量
环境分为test
,prod
,test
环境,他们请求的后台接口获取数据,不同环境的接口的URL
不同,所以要为不同环境配置不同的接口URL
,通过路径前缀进行匹配。
-
在根目录下创建
.env.development
和.env.production
文件,为了防止意外地讲一些环境变量泄漏到客户端,只有以VITE_
为前缀的环境变量才会通过import.meta.env
以字符串形式暴露给经过Vite
处理的代码使用。如:
VITE_DEV_API=/dev-api
可以通过import.meta.env.VITE_DEV_API
访问,返回/dev-api
。WY_DEV_API=/dev-api
不能通过import.meta.env.WY_DEV_API
访问,返回undefined
。
-
.env.development
文件配置(注意开发和生产环境配置不要搞反了)VITE_APP_SERVICE_URL
值更改为你自已配置的项目后端的
接口服务地址。# 使用 VITE_ 开头的变量会被 vite 暴露出来 # 定义请求的基础URL, 方便跨域请求时使用 VITE_APP_BASE_API=/dev-api # 接口服务地址 VITE_APP_SERVICE_URL= https://www.****.com/
-
.env.production 文件配置
# 使用 VITE_ 开头的变量会被 vite 暴露出来 # 定义请求的基础URL, 方便跨域请求时使用 VITE_APP_BASE_API=/pro-api # 接口服务地址 VITE_APP_SERVICE_URL= https://www.****.com/
-
测试是否配置成功,在
request.ts
中添加以下代码,看下浏览器控制台是否会输出,在项目任意模块文件中,都可以使用 通过import.meta.env.VITE_APP_BASE_API
获取值:import axios from 'axios'; import type { AxiosInstance } from 'axios'; import { ElMessage } from 'element-plus'; console.log(import.meta.env.VITE_APP_BASE_API) const request: AxiosInstance = axios.create({ baseURL: '/dev-api', timeout: 20000, // 请求超时的毫秒数,请求时间超过指定值,则请求会被中断 });
Vite配置环境模式
默认情况下,开发服务器 ( dev 命令)
运行在 development (开发)
模式,而 build
命令则运行在 production(生产)
模式。
查看package.json
,当执行 vite build
时,它会自动加载 .env.production
中可能存在的环境变量:
VITE_APP_BASE_API=/pro-api
在 utils/request.ts
中可以使用import.meta.env.VITE_APP_BASE_API
获取引用。在某些情况下,若想在 vite build
时运行不同的模式来渲染不同的标题,你可以通过传递 --mode
选项标志来覆盖命令使用的默认模式。
例如,如果你想在 production
(生产)模式下构建应用:
-
项目根目录下新建一个
.env.production
文件:
-
在
package.json
中添加一个prod
选项运行vite --mode production
命令
-
启动prod环境查看
console.log(import.meta.env.VITE_APP_BASE_API)
打印结果:
重构代理配置
重构vite.config
中的server.proxy
代理配置,在vite.config.ts
中无法通过import.meta.env.xxx
获取到VITE_
环境变量,解决此问题需要用到vite
中提供的loadEnv
方法来读取环境变量。
import { defineConfig, loadEnv } from 'vite'
// mode:获取 --mode 指定的模式,process.cwd()项目根目录,下面 `env` 相当于 `import.meta.env`
const env = loadEnv(mode, process.cwd());
- 重构
vite.config
中的server.proxy
代理配置import { fileURLToPath, URL } from 'node:url' // 1. 导入 loadEnv import { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' // 2. 向 defineConfig 传递对象改为传递方法,并返回配置对象 export default defineConfig(({ mode }) => { // mode:获取 --mode 指定的模式,process.cwd()项目根目录,下面 `env` 相当于 `import.meta.env` const env = loadEnv(mode, process.cwd()); return { // 开发服务器选项 server: { open: true, //启动服务时自动打开浏览器访问 port: 8888, //端口号, 如果端口号被占用,会自动提升1 proxy: { // '/dev-api': { // 匹配 /dev-api 开头的请求, [env.VITE_APP_BASE_API]: { // 引用变量作为key时,要加中括号[] // 目标服务器 target: env.VITE_APP_SERVICE_URL, // 开启代理 changeOrigin: true, rewrite: path => path.replace(new RegExp(`^${env.VITE_APP_BASE_API}/`), '') }, } }, plugins: [ vue(), ], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } } });
- 修改
utils/request.ts
文件配置:baseURL: import.meta.env.VITE_APP_BASE_API
import axios from 'axios'; import type { AxiosInstance } from 'axios'; import { ElMessage } from 'element-plus'; console.log(import.meta.env.VITE_APP_BASE_API) const request: AxiosInstance = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, timeout: 20000, // 请求超时的毫秒数,请求时间超过指定值,则请求会被中断 });
- 重启看一下效果,接口是否调用到