目錄
一、前言
二、uniapp官方文檔
三、舉例演示
3.1 使用說明
3.2 Content-Type
3.2.1???基本概念
??3.2.2 核心作用
3.2.3?常見?Content-Type?類型及使用場景
1)文本類
a)text/plain????
b)text/html??? ?
2)應用類
a)application/json??? ?
b)application/x-www-form-urlencoded??
3)多媒體類
image/jpeg??、??video/mp4
4)特殊類型??
? ??application/octet-stream??
3.2.4?關鍵注意事項
3.3 success參數
3.3.1 基礎回調函數寫法
3.3.2 分離函數(外部定義)
三、Promise
3.1 是什么
3.2 核心思想
3.3 ?Promise 的三種狀態?
3.4 基本用法與語法
3.4.1 創建 Promise?
3.4.2 ??處理結果??
3.4.3 鏈式調用??
?四、封裝
五、調用演示
一、前言
在前文中,我們介紹了uniapp中如何發送請求,并完成了基礎的二次封裝。雖然直接復用示例代碼可以快速上手,但面對復雜項目時,往往需要根據實際需求重新設計網絡請求模塊。
對于前端經驗較少的開發者來說,深度封裝可能具有一定挑戰性。本文旨在梳理不同項目的封裝思路,幫助大家掌握核心邏輯,從而能夠靈活應對各種業務場景的需求變化。
二、uniapp官方文檔
首先給出uniapp發送網絡請求的官網文檔,然后再帶領大家一起解讀。uni.request(OBJECT) | uni-app官網
uni.request(OBJECT)
OBJECT 參數說明
參數名 | 類型 | 必填 | 默認值 | 說明 | 平臺差異說明 |
---|---|---|---|---|---|
url | String | 是 | 開發者服務器接口地址 | ||
data | Object/String/ArrayBuffer | 否 | 請求的參數 | App 3.3.7 以下不支持 ArrayBuffer 類型 | |
header | Object | 否 | 設置請求的 header,header 中不能設置 Referer | App、H5端會自動帶上cookie,且H5端不可手動修改 | |
method | String | 否 | GET | 有效值詳見下方說明 | |
timeout | Number | 否 | 60000 | 超時時間,單位 ms | H5(HBuilderX 2.9.9+)、APP(HBuilderX 2.9.9+)、微信小程序(2.10.0)、支付寶小程序 |
dataType | String | 否 | json | 如果設為 json,會對返回的數據進行一次 JSON.parse,非 json 不會進行 JSON.parse | |
responseType | String | 否 | text | 設置響應的數據類型。合法值:text、arraybuffer | 支付寶小程序不支持 |
sslVerify | Boolean | 否 | true | 驗證 ssl 證書 | 僅App安卓端支持(HBuilderX 2.3.3+),不支持離線打包 |
withCredentials | Boolean | 否 | false | 跨域請求時是否攜帶憑證(cookies) | 僅H5支持(HBuilderX 2.6.15+) |
firstIpv4 | Boolean | 否 | false | DNS解析時優先使用ipv4 | 僅 App-Android 支持 (HBuilderX 2.8.0+) |
enableHttp2 | Boolean | 否 | false | 開啟 http2 | 微信小程序 |
enableQuic | Boolean | 否 | false | 開啟 quic | 微信小程序 |
enableCache | Boolean | 否 | false | 開啟 cache | 微信小程序、抖音小程序 2.31.0+ |
enableHttpDNS | Boolean | 否 | false | 是否開啟 HttpDNS 服務。如開啟,需要同時填入 httpDNSServiceId 。 HttpDNS 用法詳見?移動解析HttpDNS | 微信小程序 |
httpDNSServiceId | String | 否 | HttpDNS 服務商 Id。 HttpDNS 用法詳見?移動解析HttpDNS | 微信小程序 | |
enableChunked | Boolean | 否 | false | 開啟 transfer-encoding chunked | 微信小程序 |
forceCellularNetwork | Boolean | 否 | false | wifi下使用移動網絡發送請求 | 微信小程序 |
enableCookie | Boolean | 否 | false | 開啟后可在headers中編輯cookie | 支付寶小程序 10.2.33+ |
cloudCache | Object/Boolean | 否 | false | 是否開啟云加速(詳見云加速服務) | 百度小程序 3.310.11+ |
defer | Boolean | 否 | false | 控制當前請求是否延時至首屏內容渲染后發送 | 百度小程序 3.310.11+ |
success | Function | 否 | 收到開發者服務器成功返回的回調函數 | ||
fail | Function | 否 | 接口調用失敗的回調函數 | ||
complete | Function | 否 | 接口調用結束的回調函數(調用成功、失敗都會執行) |
method 有效值
注意:method有效值必須大寫,每個平臺支持的method有效值不同,詳細見下表。
method | App | H5 | 微信小程序 | 支付寶小程序 | 百度小程序 | 抖音小程序、飛書小程序 | 快手小程序 | 京東小程序 |
---|---|---|---|---|---|---|---|---|
GET | √ | √ | √ | √ | √ | √ | √ | √ |
POST | √ | √ | √ | √ | √ | √ | √ | √ |
PUT | √ | √ | √ | x | √ | √ | x | x |
DELETE | √ | √ | √ | x | √ | x | x | x |
CONNECT | x | √ | √ | x | x | x | x | x |
HEAD | √ | √ | √ | x | √ | x | x | x |
OPTIONS | √ | √ | √ | x | √ | x | x | x |
TRACE | x | √ | √ | x | x | x | x | x |
可見,請求方式主推使用GET、POST。
success 返回參數說明
參數 | 類型 | 說明 |
---|---|---|
data | Object/String/ArrayBuffer | 開發者服務器返回的數據 |
statusCode | Number | 開發者服務器返回的 HTTP 狀態碼 |
header | Object | 開發者服務器返回的 HTTP Response Header |
cookies | Array.<string> | 開發者服務器返回的 cookies,格式為字符串數組 |
三、舉例演示
3.1 使用說明
這里我們找一個免費的api接口進行測試。
mounted() {this.test()},
methods: {test() {uni.request({url: 'https://route.showapi.com/9-2?appKey=E53C00b070ce4Ee0b2B77365af35993c',method: 'POST',header: { 'content-type': 'application/x-www-form-urlencoded' }, data:{area: '重慶'},success: (res) => { console.log("請求成功",res);}})}
}
打開頁面調用控制臺看一下
調用成功!
3.2 Content-Type
3.2.1???基本概念
Content-Type?是 HTTP 協議中用于標識請求或響應體數據類型的頭部字段,屬于 MIME(多用途互聯網郵件擴展)類型的一種。其格式為:
Content-Type: <type>/<subtype>; <parameters>
??type??:主類型(如?
text
、application
、image
)?subtype??:子類型(如?
html
、json
、jpeg
)parameters??:可選參數(如?
charset=UTF-8
、boundary
?用于分段數據)
??3.2.2 核心作用
客戶端請求??:告知服務器如何解析請求體(如 JSON 或表單數據)
服務器響應??:指導客戶端(如瀏覽器)如何處理返回的數據(如渲染 HTML 或下載文件)
3.2.3?常見?Content-Type
?類型及使用場景
1)文本類
a)text/plain
????
?純文本數據,適用于日志或簡單消息。
Content-Type: text/plain; charset=UTF-8
b)text/html
??? ?
?HTML 文檔,用于網頁渲染。
// 后端設置示例
res.setHeader('Content-Type', 'text/html');
2)應用類
a)application/json
??? ?
JSON格式數據,適合傳輸結構化數據(如 API 交互)。?
前端請求示例??:
fetch('/api', {headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ key: 'value' })
});
后端接收??(Spring Boot)?
@PostMapping("/api")
public ResponseEntity<?> handleJson(@RequestBody User user) { ... }
b)application/x-www-form-urlencoded?
?
?表單默認提交格式,數據編為?
key=value&key2=value2
前端請求示例
uni.request({url: 'https://route.showapi.com/9-2?appKey=E53C00b070ce4Ee0b2B77365af35993c',method: 'POST',header: { 'content-type': 'application/x-www-form-urlencoded' }, data:{area: '重慶'},success: (res) => { console.log("請求成功",res);}
})
后端SpringBoot接收方式
- 使用?
@RequestParam
?注解??- 適用于直接接收表單字段,字段名需與前端參數名一致
-
@PostMapping("/login") public String login(@RequestParam String username, @RequestParam String password) {return "用戶名:" + username + ",密碼:" + password; }
- ??注意??:若參數名不一致,需指定?
@RequestParam("前端字段名")
- 通過?
Map
?或?LinkedHashMap
?接收- 適用于動態或大量字段的場景:
-
@PostMapping("/submit") public ResponseEntity<?> submit(@RequestParam Map<String, String> params) {System.out.println(params); // 輸出所有鍵值對return ResponseEntity.ok(params); }
- ?優點:無需預定義字段,靈活性強
- 避免使用?
@RequestBody
??x-www-form-urlencoded
?數據默認不支持?@RequestBody
?綁定到自定義對象,否則會報錯?Content-Type not supported
。若需綁定到對象,需改用?@ModelAttribute
?或直接通過字段接收。
3)
多媒體類
image/jpeg
??、??video/mp4
分別用于 JPEG 圖片和 MP4 視頻,常見于資源加載或文件下載。
響應頭示例
Content-Type: image/png
Content-Disposition: attachment; filename="example.png"
4)特殊類型??
? ??application/octet-stream
??
二進制流,適用于任意文件下載
// 后端設置
res.setHeader('Content-Type', 'application/octet-stream');
?
3.2.4?關鍵注意事項
?字符編碼??
文本類數據需指定?charset
(如?UTF-8
),避免亂碼。
??與后端的匹配??
- 使用?
@RequestBody
?接收 JSON 時,必須設置?Content-Type: application/json
。 - 表單數據需對應?
@RequestParam
?,設置?application/x-www-form-urlencoded 或 FormData
。
??錯誤處理??
- 錯誤的?
Content-Type
?可能導致 HTTP 415(不支持的媒體類型)。 - 文件上傳時若手動設置?
multipart/form-data
?的?boundary
,需確保格式正確。
Content-Type
?是 HTTP 通信的“語言協議”,正確設置可確保數據解析無誤。開發中需根據場景選擇類型:
- ??API 交互?? →?
application/json
- ??表單提交?? →?
application/x-www-form-urlencoded
- ??文件上傳?? →?
multipart/form-data
- ??資源加載?? → 對應媒體類型(如?
image/png
)
?
3.3 success參數
在uniapp中,uni.request?方法的 success 參數用于處理請求成功的回調,其寫法靈活多樣,可以根據開發場景和編碼風格選擇。以下是常見的幾種寫法及詳細說明:
3.3.1 基礎回調函數寫法
匿名函數(直接定義)
直接在?success
?屬性中定義匿名函數,適用于簡單邏輯:
uni.request({url: 'https://api.example.com/data',success: function(res) {console.log('請求成功:', res.data); // 輸出響應數據// 其他邏輯處理},fail: (err) => { console.error('請求失敗:', err); }
});
特點??:代碼直觀,適合快速開發或單次請求場景
?注意??:res
?參數包含?data
(響應體)、statusCode
(HTTP狀態碼)、header
(響應頭)等屬性
?箭頭函數(ES6語法)
使用箭頭函數簡化作用域綁定,避免?this
?指向問題
uni.request({url: 'https://api.example.com/data',success: (res) => {this.dataList = res.data; // 直接訪問組件實例的data}
});
?適用場景??:Vue 組件中需要訪問?this
?時推薦使用
3.3.2 分離函數(外部定義)
將回調邏輯抽離為獨立函數,提升代碼復用性
function handleSuccess(res) {if (res.statusCode === 200) {console.log('數據:', res.data);} else {console.error('服務器異常:', res.statusCode);}
}uni.request({url: 'https://api.example.com/data',success: handleSuccess // 傳入函數引用
});
- ??優點??:邏輯清晰,便于復用和單元測試。
- ??擴展??:可結合錯誤狀態碼統一處理(如 401 跳轉登錄頁)
三、Promise
3.1 是什么
Promise 是 JavaScript 中用于處理異步操作的核心對象,它代表一個異步操作的最終完成(或失敗)及其結果值。以下是關于 Promise 的詳細解析,綜合了多個搜索結果中的核心信息。
Promise 是一個容器,用于封裝異步操作(如網絡請求、定時器、文件讀取等),并通過狀態管理來追蹤其完成情況。它允許開發者以更清晰的方式處理異步邏輯,避免傳統的“回調地獄”。
3.2 核心思想
延遲綁定??:Promise 將異步操作的結果與處理邏輯分離,通過鏈式調用(.then()、.catch())注冊回調函數,而非嵌套回調。
狀態不可逆??:Promise 的狀態一旦改變(從?pending
?到?fulfilled
?或?rejected
),便不可再變。
3.3 ?Promise 的三種狀態?
??Pending(進行中)??
初始狀態,表示異步操作尚未完成。
??Fulfilled(已成功)??
異步操作成功完成,通過?resolve(value)
?觸發,此時?.then()
?的回調函數會被執行。
Rejected(已失敗)??
異步操作失敗,通過?reject(error)
?觸發,此時?.catch()
?或?.then()
?的第二個參數會捕獲錯誤。
3.4 基本用法與語法
3.4.1 創建 Promise?
const promise = new Promise((resolve, reject) => {// 異步操作(如 setTimeout、AJAX)if (/* 成功 */) resolve(value); // 狀態變為 fulfilledelse reject(new Error('失敗原因')); // 狀態變為 rejected
});
例如:
return new Promise((resolve, reject) => {uni.request({...options,success: (res) => {if(res.data.code === 1) {resolve(res.data);}else{// 統一錯誤處理(如token過期)if(res.data.code === -1 || res.data.code === -3) {uni.navigateTo({url:'/pages/login/login'})}reject(res.data);}},fail: (err) => {uni.showToast({ title: '網絡錯誤', icon: 'none' });reject(err);}})
})
3.4.2 ??處理結果??
??
.then()
??:處理成功狀態,接收?resolve
?傳遞的值
promise.then((result) => console.log(result));
.catch()
??:處理失敗狀態,捕獲?reject
?或代碼拋出的錯誤?
promise.catch((error) => console.error(error));
.finally()
??:無論成功或失敗都會執行,常用于清理邏輯?
3.4.3 鏈式調用??
Promise 的?.then()
?返回一個新的 Promise,支持連續調用:
fetchData().then(processData).then(saveData).catch(handleError);
?
?四、封裝
我們使用Promise來封裝請求。具體的,先給出結果,然后再一點點講。
定義一個request.js文件
名字不重要,叫什么都可以,目的是通過Promise封裝我們的uni.request函數。
import {BASE_URL} from '@/common/config.js'export const myRequest = (options) => {// 自動拼接請求地址options.url = BASE_URL + options.url;// 默認請求頭options.header = {'Authorization': uni.getStorageSync('token') || '','Content-Type': 'application/json',...options.header // 允許調用者覆蓋};// return new Promise((resolve, reject) => {uni.request({...options,success: (res) => {if(res.data.code === 1) {resolve(res.data);}else{// 統一錯誤處理(如token過期)if(res.data.code === -1 || res.data.code === -3) {uni.navigateTo({url:'/pages/login/login'})}reject(res.data);}},fail: (err) => {uni.showToast({ title: '網絡錯誤', icon: 'none' });reject(err);}})})}
解讀:?
在這個js文件中,我們通過ES6 模塊化規范的導入語法,導入了根目錄下的common包中的config.js文件中的BASE_URL。這樣做的目的是通過某一個配置文件來集中定義一些通用的配置,方便修改和維護。
config.js
// common/config.js
export const BASE_URL = 'http://106.75.224.22:8090'; // 基礎域名
api統一封裝
import {myRequest} from '@/utils/request.js'export const loginApi = (username, password) => {return myRequest({url: '/api/login',method: 'POST',data: {username, password}})
};export const getStatisticApi = (date) => {const params = date ? { date } : {};return myRequest({url: '/api/keepAccount/getStatistic',method: 'GET',data: params,})
};export const addAccountApi = (event, money, type, happenDate) => {return myRequest({url: '/api/keepAccount/addAccount',method: 'POST',data: {event,money,type,happenDate}})
};export const getListApi = (date) => {return myRequest({url: '/api/keepAccount/getList',method: 'GET',data: {date}})
};
五、調用演示
handleLogin() {if (!this.canLogin) return// 這里替換為實際的登錄邏輯uni.showLoading({title: '登錄中...'})// 調用登錄接口loginApi(this.username, this.password).then(res => {uni.showToast({title: '登錄成功',icon: 'success'});// 保存到本地token// 注意這里為什么不是res.data.data。是因為我們request.js封裝的時候,返回的res對象其實是res.data,而非最初的res對象。uni.setStorageSync("token", res.data);setTimeout(() => {uni.reLaunch({url: '/pages/index/index'});}, 1500);}).catch(error => {console.error('登錄失敗:', error);uni.showToast({title: error.msg,icon: 'none'});}).finally(() => {uni.hideLoading();});console.log("你好");
}
在這個handleLogin()方法中,??控制臺會先打印“你好”,然后才會處理請求的返回結果?。
因為它是同步代碼,而 Promise 回調是異步的微任務。
網絡請求的響應時間不確定,但其回調一定在同步代碼之后執行。
如果需要強制等待請求完成再執行后續邏輯,需使用?async/await
?改寫:
async handleLogin() {console.log("你好"); // 仍會先執行await loginApi(this.username, this.password).then(/* ... */);console.log("請求完成后才執行"); // 微任務之后
}
?怎么樣,現在是不是清楚整個封裝流程了?🫠
如果有什么疑問或者表述錯誤地方,也歡迎小伙伴們一起溝通、交流和指正~~~