npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kinngyo/wx-request

v0.0.6

Published

基于wx.request封装的微信小程序网络请求,支持请求拦截、响应拦截、节流功能

Readme

@kinngyo/wx-request

基于 wx.request 封装的微信小程序网络请求工具,提供实例配置、拦截器、请求节流、取消请求和 TypeScript 类型支持。

特性

  • 基于微信小程序原生 wx.request
  • 支持 baseURLheadertimeout 等默认配置
  • 支持请求拦截器和响应拦截器
  • 支持业务自定义参数,方便控制 loading、toast、鉴权等逻辑
  • 支持请求节流,避免短时间重复提交
  • 支持 AbortControllerrequestTask.abort() 取消请求
  • 支持 pureRequest 跳过拦截器和节流
  • TypeScript 友好

安装

npm i @kinngyo/wx-request

使用环境

@kinngyo/wx-request 面向微信小程序环境,依赖全局 wx.request

| 环境 | 支持情况 | | ---------- | ---------- | | 微信小程序 | 支持 | | TypeScript | 支持 | | 浏览器 | 非目标环境 | | Node.js | 非目标环境 |

目录

快速开始

import Request from '@kinngyo/wx-request'

const request = new Request({
    baseURL: 'https://api.example.com',
})

const result = await request.request({
    url: '/user',
    method: 'GET',
})

console.log(result.response?.data)

请求成功时,结果会放在 result.response 中;请求失败时会进入 catch,错误信息中会带上本次请求配置。

try {
    const result = await request.request({
        url: '/user',
    })

    console.log(result.response?.data)
} catch (error) {
    console.log(error)
}

url 支持相对地址和完整地址。相对地址会和 baseURL 拼接,完整地址会直接使用。

await request.request({ url: '/user' })

await request.request({ url: 'https://other.example.com/user' })

推荐项目封装

实际项目中建议单独创建一个请求模块,例如 utils/request.ts,统一处理基础地址、token、错误提示和快捷方法。

import Request, {
    Interceptor,
    type CustomRequestConfig,
    type CustomRequestResult,
    type RequestData,
    type RequestInputConfig,
    type RequestResult,
} from '@kinngyo/wx-request'

interface AppConfig {
    showErrorToast?: boolean
    needToken?: boolean
}

type AppRequestConfig<T extends RequestData = RequestData> =
    CustomRequestConfig<AppConfig, T>
type AppRequestResult<T extends RequestData = RequestData> =
    CustomRequestResult<AppConfig, T>
type ApiConfig<T extends RequestData = RequestData> = Omit<
    RequestInputConfig<T> & Partial<AppConfig>,
    'url' | 'method' | 'data'
>

const request = new Request<AppConfig>({
    baseURL: 'https://api.example.com',
    timeout: 10000,
    showErrorToast: true,
    needToken: true,
})

const requestInterceptor = new Interceptor<AppRequestConfig>()

requestInterceptor.add({
    onFulfilled: config => {
        if (!config.needToken) {
            return config
        }

        return {
            ...config,
            header: {
                ...(config.header ?? {}),
                Authorization: `Bearer ${wx.getStorageSync('token')}`,
            },
        }
    },
})

const responseInterceptor = new Interceptor<AppRequestResult>()

responseInterceptor.add({
    onFulfilled: result => result,
    onRejected: error => {
        if (error.config?.showErrorToast) {
            wx.showToast({
                title: '请求失败',
                icon: 'none',
            })
        }

        return Promise.reject(error)
    },
})

request.interceptors.use(requestInterceptor, responseInterceptor)

/**
 * 发送 GET 请求
 * @param url 请求地址
 * @param data 请求参数
 * @param config 请求配置
 * @returns 请求结果
 */
export function get<T extends RequestData = RequestData>(
    url: string,
    data?: T,
    config: ApiConfig<T> = {},
): Promise<RequestResult<T>> {
    return request.request<T>({
        ...config,
        url,
        data,
        method: 'GET',
    })
}

/**
 * 发送 POST 请求
 * @param url 请求地址
 * @param data 请求数据
 * @param config 请求配置
 * @returns 请求结果
 */
export function post<T extends RequestData = RequestData>(
    url: string,
    data?: T,
    config: ApiConfig<T> = {},
): Promise<RequestResult<T>> {
    return request.request<T>({
        ...config,
        url,
        data,
        method: 'POST',
    })
}

export default request

业务页面中直接使用封装后的方法。

import { get, post } from '@/utils/request'

const userResult = await get('/user', { id: 1 })
const saveResult = await post('/user/save', { name: 'kinngyo' })

console.log(userResult.response?.data)
console.log(saveResult.response?.data)

默认配置

构造函数接收默认请求配置。调用 request 时,单次请求配置会覆盖默认配置,header 内部也遵循这个规则。

import Request from '@kinngyo/wx-request'

const request = new Request({
    baseURL: 'https://api.example.com',
    header: {
        token: 'default-token',
    },
})

await request.request({
    url: '/profile',
    header: {
        token: 'page-token',
        traceId: 'request-id',
    },
})

最终请求地址为 https://api.example.com/profile,并且 header.token 会使用 page-token

自定义参数

自定义参数会和请求配置一起合并,并保留在最终 config 中,适合控制 loading、toast、鉴权、数据格式化等业务行为。

import Request from '@kinngyo/wx-request'

interface AppConfig {
    showLoading?: boolean
    showErrorToast?: boolean
    needToken?: boolean
}

const request = new Request<AppConfig>({
    baseURL: 'https://api.example.com',
    showLoading: true,
    showErrorToast: true,
    needToken: true,
})

const result = await request.request({
    url: '/user',
    showLoading: false,
})

console.log(result.config.showLoading)

拦截器

Interceptor 用于定义可复用的拦截器组。请求拦截器适合处理 token、loading、参数转换;响应拦截器适合处理错误提示、登录失效、响应格式化。

import Request, {
    Interceptor,
    type CustomRequestConfig,
    type CustomRequestResult,
} from '@kinngyo/wx-request'

interface AppConfig {
    showLoading?: boolean
}

type AppRequestConfig = CustomRequestConfig<AppConfig>
type AppRequestResult = CustomRequestResult<AppConfig>

const request = new Request<AppConfig>({
    baseURL: 'https://api.example.com',
    showLoading: true,
})

const requestInterceptor = new Interceptor<AppRequestConfig>()

requestInterceptor.add({
    onFulfilled: config => {
        if (config.showLoading) {
            wx.showLoading({ title: '加载中' })
        }

        return {
            ...config,
            header: {
                ...(config.header ?? {}),
                token: wx.getStorageSync('token'),
            },
        }
    },
    onRejected: error => Promise.reject(error),
})

const responseInterceptor = new Interceptor<AppRequestResult>()

responseInterceptor.add({
    onFulfilled: result => {
        wx.hideLoading()
        return result
    },
    onRejected: error => {
        wx.hideLoading()
        return Promise.reject(error)
    },
})

request.interceptors.request.use(requestInterceptor)
request.interceptors.response.use(responseInterceptor)

也可以用快捷方式同时接入请求和响应拦截器。

request.interceptors.use(requestInterceptor, responseInterceptor)

同一个拦截器组可以复用到多个请求实例。

const userRequest = new Request<AppConfig>({
    baseURL: 'https://user.example.com',
})

const orderRequest = new Request<AppConfig>({
    baseURL: 'https://order.example.com',
})

userRequest.interceptors.request.use(requestInterceptor)
orderRequest.interceptors.request.use(requestInterceptor)

请求节流

Throttle 会按请求方法、完整地址和请求数据生成唯一 key。在同一时间窗口内超过最大次数时,不会继续调用 wx.request

import Request, { Throttle } from '@kinngyo/wx-request'

const throttle = new Throttle({
    timeWindow: 500,
    maxCount: 3,
})

const request = new Request({
    baseURL: 'https://api.example.com',
    throttle,
})

await request.request({
    url: '/submit',
    method: 'POST',
    data: { name: 'kinngyo' },
})

默认 key 由 method + url + JSON.stringify(data) 生成。需要按业务字段节流时,可以自定义 getKey

const throttle = new Throttle({
    timeWindow: 1000,
    maxCount: 1,
    getKey(config) {
        return `${config.method ?? 'GET'}:${config.url}:${config.data.orderId}`
    },
})

触发节流后默认返回 rejected Promise,也可以通过 response 自定义行为。

const throttle = new Throttle({
    timeWindow: 500,
    maxCount: 3,
    response(config) {
        return (_resolve, reject) => {
            reject({
                error: '频繁操作,请稍后再试!',
                config,
            })
        }
    },
})

单次请求也可以覆盖默认节流器。

await request.request({
    url: '/submit',
    method: 'POST',
    throttle: new Throttle({
        timeWindow: 1000,
        maxCount: 1,
    }),
})

需要重置节流状态时,可以清理指定请求 key 或全部记录。

throttle.clear()

取消请求

库内提供轻量版 AbortController,底层会桥接到小程序原生 RequestTask.abort()

import Request, { AbortController } from '@kinngyo/wx-request'

const request = new Request({
    baseURL: 'https://api.example.com',
})

const controller = new AbortController()

request.request({
    url: '/long-task',
    signal: controller.signal,
})

controller.abort()

页面卸载时取消请求是常见用法。

import Request, { AbortController } from '@kinngyo/wx-request'

const request = new Request({
    baseURL: 'https://api.example.com',
})

Page({
    controller: new AbortController(),

    onLoad() {
        request.request({
            url: '/page-data',
            signal: this.controller.signal,
        })
    },

    onUnload() {
        this.controller.abort()
    },
})

如果运行环境已经提供标准 AbortController,也可以直接传入原生 controller.signal

获取 requestTask

通过 task 可以拿到 wx.request 返回的请求任务。

let task: WechatMiniprogram.RequestTask | undefined

request.request({
    url: '/long-task',
    task(requestTask) {
        task = requestTask
    },
})

task?.abort()

signaltask 可以同时使用。

import { AbortController } from '@kinngyo/wx-request'

const controller = new AbortController()
let task: WechatMiniprogram.RequestTask | undefined

request.request({
    url: '/long-task',
    signal: controller.signal,
    task(requestTask) {
        task = requestTask
    },
})

controller.abort()
task?.abort()

pureRequest

pureRequest 会复用默认配置并直接调用 wx.request,不会执行拦截器,也不会触发节流逻辑,但仍然支持 signaltask

const result = await request.pureRequest({
    url: '/health',
})

console.log(result.response?.statusCode)

API

Request

const request = new Request(defaultConfig)

| 参数 | 类型 | 必填 | 说明 | | ------------- | ----------------------------------- | ---- | ------------ | | defaultConfig | RequestDefaultConfig & Partial<T> | 否 | 默认请求配置 |

request.request

request.request(config)

| 参数 | 类型 | 必填 | 说明 | | ------ | --------------------------------- | ---- | ------------ | | config | RequestInputConfig & Partial<T> | 是 | 单次请求配置 |

返回 Promise<RequestResult>

request.pureRequest

request.pureRequest(config)

| 参数 | 类型 | 必填 | 说明 | | ------ | --------------------------------- | ---- | ------------ | | config | RequestInputConfig & Partial<T> | 是 | 单次请求配置 |

返回 Promise<RequestResult>。不会执行拦截器和请求节流。

request.interceptors

request.interceptors.request.use(requestInterceptor)
request.interceptors.response.use(responseInterceptor)
request.interceptors.use(requestInterceptor, responseInterceptor)

| 方法 | 说明 | | -------------- | ------------------------------ | | request.use | 添加请求拦截器 | | response.use | 添加响应拦截器 | | use | 同时添加请求拦截器和响应拦截器 |

Interceptor

const interceptor = new Interceptor()
interceptor.add(plugin)

| 参数 | 类型 | 必填 | 说明 | | ------ | ---------------------- | ---- | ---------- | | plugin | InterceptorPlugin<T> | 是 | 拦截器插件 |

Throttle

const throttle = new Throttle(config)

| 参数 | 类型 | 必填 | 默认值 | 说明 | | ---------- | ----------------------------------- | ---- | ---------------- | ---------------------------- | | timeWindow | number | 否 | 500 | 节流时间窗口,单位 ms | | maxCount | number | 否 | 3 | 时间窗口内允许的最大请求次数 | | response | ResponseCallback | 否 | rejected Promise | 触发节流后的响应行为 | | getKey | (config: RequestConfig) => string | 否 | 内置 key 规则 | 自定义请求唯一标识 |

AbortController

const controller = new AbortController()

controller.abort()

| 属性/方法 | 说明 | | --------- | ------------ | | signal | 取消请求信号 | | abort() | 取消请求 |

类型说明

请求配置

| 参数 | 类型 | 必填 | 说明 | | -------- | ------------------------------------------- | ---- | ------------ | | url | string | 是 | 请求地址 | | baseURL | string | 否 | 请求基础地址 | | method | WechatMiniprogram.RequestOption["method"] | 否 | 请求方法 | | data | RequestData | 否 | 请求数据 | | header | WechatMiniprogram.IAnyObject | 否 | 请求头 | | timeout | number | 否 | 超时时间 | | throttle | Throttle | 否 | 请求节流器 | | signal | AbortSignalLike | 否 | 取消请求信号 | | task | (requestTask: RequestTask) => void | 否 | 获取请求任务 |

除上表字段外,也支持 wx.request 的其他原生参数。

请求结果

interface RequestResult<T, U> {
    config: U
    response?: WechatMiniprogram.RequestSuccessCallbackResult<T>
    error?: WechatMiniprogram.RequestFailCallbackErr
}

| 字段 | 说明 | | -------- | -------------------- | | config | 最终请求配置 | | response | 请求成功时的响应结果 | | error | 请求失败时的错误结果 |

FAQ

为什么不是直接返回 data?

为了保留微信小程序完整响应信息,返回值中会保留 statusCodeheadercookiesdata 和最终请求配置。业务中可以通过 result.response?.data 取数据。

request 和 pureRequest 有什么区别?

request 会执行请求拦截器、请求节流、真实请求和响应拦截器。pureRequest 只执行配置合并、地址拼接和真实请求。

请求节流为什么没有生效?

常见原因是每次请求的 data 中包含 timestamp、随机数、traceId 等变化字段,导致生成的 key 不一致。此时建议使用 getKey 自定义稳定 key。

如何统一处理 token 失效?

建议在响应拦截器中判断业务状态码,清理登录态并跳转登录页。

responseInterceptor.add({
    onFulfilled: result => {
        if (result.response?.data?.code === 401) {
            wx.removeStorageSync('token')
            wx.navigateTo({ url: '/pages/login/index' })
        }

        return result
    },
})

如何在页面卸载时取消请求?

创建 AbortController,请求时传入 signal,在页面 onUnload 中调用 abort()

导出内容

import Request, {
    AbortController,
    AbortSignal,
    Interceptor,
    Throttle,
    buildFullPath,
    combineURLs,
    createRequestKey,
    isAbsoluteURL,
    resolveRequestConfig,
} from '@kinngyo/wx-request'

import type {
    AbortSignalLike,
    CustomRequestConfig,
    CustomRequestResult,
    InterceptorCallback,
    InterceptorConfig,
    InterceptorPlugin,
    Optional,
    RequestConfig,
    RequestResult,
    RequestTask,
    ResponseCallback,
    ThrottleConfig,
} from '@kinngyo/wx-request'

核心逻辑

核心设计参考 axios 的请求模型:先合并配置,再按 Promise 链执行请求拦截器、真实请求和响应拦截器。底层请求能力来自微信小程序的 wx.request,取消请求会落到原生 RequestTask.abort()

一次 request() 请求会按下面顺序执行:

  1. 合并默认配置和本次请求配置
  2. 执行请求拦截器
  3. 拼接 baseURLurl
  4. 判断是否触发请求节流
  5. 调用 wx.request
  6. 绑定 signalrequestTask
  7. 执行响应拦截器

对应到内部实现:

  • resolveRequestConfig:合并默认配置和本次请求配置
  • Interceptor:维护请求/响应拦截器组
  • dispatchRequest:处理完整地址、节流、取消请求和 wx.request
  • request:组装完整 Promise 链
  • pureRequest:跳过拦截器和节流,直接进入 dispatchRequest(false)