目錄
- 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 => {// 在此處可向請求頭加上認證tokenreturn 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,paramrequest({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) // Promisereturn 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-hostserver: {open: true, //啟動服務時自動打開瀏覽器訪問port: 8888, //端口號, 如果端口號被占用,會自動提升1proxy: { // ++++++++++++++++++ 解決跨域問題'/dev-api': { // 匹配 /dev-api 開頭的請求,// 目標服務器, 代理訪問到https://mock.mengxuegu.com/mock/6621e5cbfaa39b4567596484/adminPowertarget: 'https://mock.mengxuegu.com/mock/6621e5cbfaa39b4567596484/adminPower',// 開啟代理:在本地會創建一個虛擬服務端,然后發送請求的數據,并同時接收請求的數據,// 這樣服務端和服務端進行數據的交互就不會有跨域問題changeOrigin: true,// 將 /dev-api 替換為 '',也就是 /dev-api 會移除,// 如 /dev-api/test 代理到https://mock.mengxuegu.com/mock/6621e5cbfaa39b4567596484/adminPowerrewrite: (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, //端口號, 如果端口號被占用,會自動提升1proxy: {// '/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, // 請求超時的毫秒數,請求時間超過指定值,則請求會被中斷 });
- 重啟看一下效果,接口是否調用到