您好,登录后才能下订单哦!
在小程序开发中,网络请求是与后端交互的核心功能。微信小程序提供了wx.request
API用于发起网络请求,但在实际项目中直接使用原生API会面临诸多问题。本文将详细介绍如何对小程序中的网络请求进行二次封装,打造一个功能完善、易于维护的请求库。
直接使用wx.request
存在以下问题:
二次封装可以带来以下优势:
我们先创建一个基础的请求类,封装wx.request
的基本功能:
// utils/request.js
class Request {
constructor(config = {}) {
// 默认配置
this.defaultConfig = {
baseURL: '', // 基础路径
timeout: 60000, // 超时时间
header: {
'content-type': 'application/json' // 默认请求头
}
}
// 合并配置
this.config = Object.assign({}, this.defaultConfig, config)
}
request(options) {
// 合并请求配置
const mergedOptions = Object.assign({}, this.config, options)
return new Promise((resolve, reject) => {
wx.request({
...mergedOptions,
success: (res) => {
resolve(res.data)
},
fail: (err) => {
reject(err)
}
})
})
}
get(url, data, options = {}) {
return this.request({
url,
data,
method: 'GET',
...options
})
}
post(url, data, options = {}) {
return this.request({
url,
data,
method: 'POST',
...options
})
}
// 其他HTTP方法...
}
// 创建实例并导出
const request = new Request({
baseURL: 'https://api.example.com'
})
export default request
使用方式:
import request from '@/utils/request'
// GET请求
request.get('/user/info', {id: 123}).then(res => {
console.log(res)
})
// POST请求
request.post('/user/create', {name: '张三'}).then(res => {
console.log(res)
})
请求拦截器可以在请求发出前对配置进行修改或执行一些公共操作:
class Request {
constructor(config = {}) {
// ...其他代码
this.interceptors = {
request: [],
response: []
}
}
// 添加请求拦截器
useRequestInterceptor(fulfilled, rejected) {
this.interceptors.request.push({
fulfilled,
rejected
})
}
// 添加响应拦截器
useResponseInterceptor(fulfilled, rejected) {
this.interceptors.response.push({
fulfilled,
rejected
})
}
async request(options) {
// 请求拦截器处理
let requestOptions = Object.assign({}, this.config, options)
for (const interceptor of this.interceptors.request) {
try {
requestOptions = await interceptor.fulfilled(requestOptions) || requestOptions
} catch (error) {
interceptor.rejected(error)
return Promise.reject(error)
}
}
return new Promise((resolve, reject) => {
wx.request({
...requestOptions,
success: async (res) => {
// 响应拦截器处理
let response = res
for (const interceptor of this.interceptors.response) {
try {
response = await interceptor.fulfilled(response) || response
} catch (error) {
interceptor.rejected(error)
reject(error)
return
}
}
resolve(response.data)
},
fail: (err) => {
reject(err)
}
})
})
}
}
使用拦截器示例:
// 添加请求拦截器 - 添加token
request.useRequestInterceptor(config => {
const token = wx.getStorageSync('token')
if (token) {
config.header = config.header || {}
config.header.Authorization = `Bearer ${token}`
}
return config
})
// 添加响应拦截器 - 处理错误状态码
request.useResponseInterceptor(response => {
if (response.statusCode !== 200) {
throw new Error(`请求失败,状态码:${response.statusCode}`)
}
return response
}, error => {
wx.showToast({
title: '网络错误',
icon: 'none'
})
return Promise.reject(error)
})
响应拦截器可以统一处理响应数据、错误等:
// 在Request类中添加响应拦截器支持
// 上面已经包含在request方法中
// 使用示例:统一处理业务错误码
request.useResponseInterceptor(response => {
const { code, message } = response.data
if (code !== 0) {
wx.showToast({
title: message || '业务错误',
icon: 'none'
})
throw new Error(message || '业务错误')
}
return response
})
完善的错误处理是网络请求的关键,我们需要处理以下几种错误:
class Request {
constructor(config = {}) {
// ...其他代码
// 默认错误处理
this.defaultErrorHandler = (error) => {
console.error('Request Error:', error)
wx.showToast({
title: '网络请求失败',
icon: 'none'
})
}
}
setErrorHandler(handler) {
this.defaultErrorHandler = handler
}
async request(options) {
try {
// ...拦截器处理
const response = await new Promise((resolve, reject) => {
const requestTask = wx.request({
...requestOptions,
success: resolve,
fail: reject
})
// 超时处理
if (requestOptions.timeout) {
setTimeout(() => {
requestTask.abort()
reject(new Error('请求超时'))
}, requestOptions.timeout)
}
})
// ...响应拦截器处理
return response.data
} catch (error) {
this.defaultErrorHandler(error)
return Promise.reject(error)
}
}
}
在某些场景下,我们需要取消正在进行的请求:
class Request {
constructor(config = {}) {
// ...其他代码
this.requestTasks = new Map()
}
request(options) {
return new Promise((resolve, reject) => {
const requestTask = wx.request({
...options,
success: (res) => {
this.requestTasks.delete(options.url)
resolve(res)
},
fail: (err) => {
this.requestTasks.delete(options.url)
reject(err)
}
})
// 存储请求任务
this.requestTasks.set(options.url, requestTask)
})
}
// 取消请求
cancelRequest(url) {
const task = this.requestTasks.get(url)
if (task) {
task.abort()
this.requestTasks.delete(url)
}
}
// 取消所有请求
cancelAllRequests() {
this.requestTasks.forEach(task => {
task.abort()
})
this.requestTasks.clear()
}
}
使用示例:
// 发起请求
const promise = request.get('/api/data')
// 取消请求
request.cancelRequest('/api/data')
// 取消所有请求
request.cancelAllRequests()
对于一些不常变的数据,可以添加缓存功能:
class Request {
constructor(config = {}) {
// ...其他代码
this.cache = new Map()
this.defaultCacheConfig = {
enable: false,
expire: 5 * 60 * 1000 // 默认5分钟
}
}
async get(url, data, options = {}) {
const cacheKey = this.generateCacheKey(url, data)
const cacheConfig = Object.assign({}, this.defaultCacheConfig, options.cache)
// 检查缓存
if (cacheConfig.enable && this.cache.has(cacheKey)) {
const { expireTime, data } = this.cache.get(cacheKey)
if (Date.now() < expireTime) {
return Promise.resolve(data)
}
this.cache.delete(cacheKey)
}
// 发起请求
return this.request({
url,
data,
method: 'GET',
...options
}).then(res => {
// 缓存结果
if (cacheConfig.enable) {
this.cache.set(cacheKey, {
data: res,
expireTime: Date.now() + cacheConfig.expire
})
}
return res
})
}
// 生成缓存key
generateCacheKey(url, data) {
return `${url}:${JSON.stringify(data)}`
}
// 清除缓存
clearCache(key) {
if (key) {
this.cache.delete(key)
} else {
this.cache.clear()
}
}
}
使用示例:
// 启用缓存
request.get('/api/data', null, {
cache: {
enable: true,
expire: 10 * 60 * 1000 // 10分钟
}
})
// 清除特定缓存
request.clearCache('/api/data:null')
// 清除所有缓存
request.clearCache()
为我们的请求库添加TypeScript类型支持:
// types/request.d.ts
interface RequestConfig {
baseURL?: string
timeout?: number
header?: Record<string, string>
data?: any
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'TRACE' | 'CONNECT'
dataType?: string
responseType?: string
enableCache?: boolean
cacheExpire?: number
}
interface Interceptor<V> {
fulfilled: (value: V) => V | Promise<V>
rejected?: (error: any) => any
}
declare class Request {
constructor(config?: RequestConfig)
request<T = any>(options: RequestConfig): Promise<T>
get<T = any>(url: string, data?: any, options?: RequestConfig): Promise<T>
post<T = any>(url: string, data?: any, options?: RequestConfig): Promise<T>
put<T = any>(url: string, data?: any, options?: RequestConfig): Promise<T>
delete<T = any>(url: string, data?: any, options?: RequestConfig): Promise<T>
useRequestInterceptor(fulfilled: (config: RequestConfig) => RequestConfig | Promise<RequestConfig>, rejected?: (error: any) => any): void
useResponseInterceptor(fulfilled: (response: any) => any, rejected?: (error: any) => any): void
cancelRequest(url: string): void
cancelAllRequests(): void
clearCache(key?: string): void
}
declare const request: Request
export default request
class Request {
constructor(config = {}) {
// ...其他代码
this.pendingRequests = new Map()
}
async request(options) {
const requestKey = this.generateRequestKey(options)
// 检查是否有相同的请求正在处理
if (this.pendingRequests.has(requestKey)) {
return this.pendingRequests.get(requestKey)
}
const promise = this._request(options).finally(() => {
this.pendingRequests.delete(requestKey)
})
this.pendingRequests.set(requestKey, promise)
return promise
}
// 生成请求唯一key
generateRequestKey(options) {
return `${options.method}:${options.url}:${JSON.stringify(options.data)}`
}
// 预加载
prefetch(url, data = {}, options = {}) {
return this.get(url, data, {
...options,
enableCache: true
})
}
}
为我们的请求库编写测试用例:
// request.test.js
describe('Request', () => {
let request
beforeEach(() => {
request = new Request({
baseURL: 'https://jsonplaceholder.typicode.com'
})
})
it('should make GET request', async () => {
const data = await request.get('/todos/1')
expect(data).toHaveProperty('id')
})
it('should handle errors', async () => {
await expect(request.get('/invalid-url')).rejects.toThrow()
})
it('should work with interceptors', async () => {
request.useRequestInterceptor(config => {
config.header = config.header || {}
config.header['X-Test'] = 'test'
return config
})
request.useResponseInterceptor(response => {
response.data.test = true
return response
})
const data = await request.get('/todos/1')
expect(data.test).toBe(true)
})
// 更多测试用例...
})
”`javascript // utils/request.js
class Request { constructor(config = {}) { // 默认配置 this.defaultConfig = { baseURL: “, timeout: 60000, header: { ‘content-type’: ‘application/json’ } }
// 合并配置
this.config = Object.assign({}, this.defaultConfig, config)
// 拦截器
this.interceptors = {
request: [],
response: []
}
// 请求任务
this.requestTasks = new Map()
// 请求缓存
this.cache = new Map()
this.defaultCacheConfig = {
enable: false,
expire: 5 * 60 * 1000
}
// 默认错误处理
this.defaultErrorHandler = (error) => {
console.error('Request Error:', error)
wx.showToast({
title: '网络请求失败',
icon: 'none'
})
}
}
// 设置错误处理器 setErrorHandler(handler) { this.defaultErrorHandler = handler }
// 添加请求拦截器 useRequestInterceptor(fulfilled, rejected) { this.interceptors.request.push({ fulfilled, rejected }) }
// 添加响应拦截器 useResponseInterceptor(fulfilled, rejected) { this.interceptors.response.push({ fulfilled, rejected }) }
// 核心请求方法 async request(options) { try { // 合并配置 let requestOptions = Object.assign({}, this.config, options)
// 请求拦截器
for (const interceptor of this.interceptors.request) {
try {
requestOptions = await interceptor.fulfilled(requestOptions) || requestOptions
} catch (error) {
interceptor.rejected(error)
return Promise.reject(error)
}
}
// 生成缓存key
const cacheKey = this.generateCacheKey(requestOptions)
const cacheConfig = Object.assign({}, this.defaultCacheConfig, requestOptions.cache)
// 检查缓存
if (requestOptions.method === 'GET' && cacheConfig.enable && this.cache.has(cacheKey)) {
const { expireTime, data } = this.cache.get(cacheKey)
if (Date.now() < expireTime) {
return Promise.resolve(data)
}
this.cache.delete(cacheKey)
}
// 发起请求
const response = await new Promise((resolve, reject) => {
const requestTask = wx.request({
...requestOptions,
success: resolve,
fail: reject
})
// 存储请求任务
this.requestTasks.set(cacheKey, requestTask)
// 超时处理
if (requestOptions.timeout) {
setTimeout(() => {
requestTask.abort()
reject(new Error('请求超时'))
}, requestOptions.timeout)
}
})
// 响应拦截器
let processedResponse = response
for (const interceptor of this.interceptors.response) {
try {
processedResponse = await interceptor.fulfilled(processedResponse) || processedResponse
} catch (error) {
interceptor.rejected(error)
return Promise.reject(error)
}
}
// 缓存响应数据
if (requestOptions.method === 'GET' && cacheConfig.enable) {
this.cache.set(cacheKey, {
data: processedResponse.data,
expireTime: Date.now() + cacheConfig.expire
})
}
return processedResponse.data
} catch (error) {
this.defaultErrorHandler(error)
return Promise.reject(error)
} finally {
// 清理请求任务
const cacheKey = this.generateCacheKey(options)
this.requestTasks.delete(cacheKey)
}
}
// HTTP方法快捷方式 get(url, data, options = {}) { return this.request({ url, data, method: ‘GET’, …options })
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。