Skip to content

Instantly share code, notes, and snippets.

View suhaotian's full-sized avatar
🎯
Focusing

su suhaotian

🎯
Focusing
View GitHub Profile
@suhaotian
suhaotian / axios-vs-xior.md
Created May 24, 2025 05:11
axios 对比 xior.js

xior.js 是一个轻量级的 HTTP 客户端库,专门为现代 JavaScript 环境设计。这个库的核心理念是提供一个比 axios 更简洁、更现代的替代方案。

xior 采用了原生的 fetch API 作为底层实现,同时保持了与 axios 相似的 API 设计。开发者可以享受 fetch 的现代特性,比如更好的流处理和原生 Promise 支持,但不需要放弃 axios 那种熟悉的拦截器、请求/响应转换等功能。

这个库的设计哲学体现在几个方面:体积更小、性能更好、TypeScript 原生支持。相比 axios 的庞大体积,xior 显著减少了包大小,这对于注重性能的现代 Web 应用尤其重要。

xior 支持请求和响应拦截器、自动 JSON 处理、错误处理机制,以及各种常见的 HTTP 客户端功能。它既可以在浏览器环境中运行,也支持 Node.js 环境,提供了统一的 API 体验。

从技术演进的角度看,xior 代表了 HTTP 客户端库的一个新方向:不是完全重新发明轮子,而是站在现有标准(fetch API)之上,提供更符合现代开发需求的抽象层。这种设计让开发者既能享受标准 API 的稳定性,又不失便利性和功能完整性。

@suhaotian
suhaotian / get-bundle-size.sh
Last active March 14, 2025 12:04
Get your lib bundle size with bun
#!/bin/bash
# Create a temporary file
TMP_FILE=$(mktemp)
# Build and save output to temp file
bun build dist/index.mjs --minify > "$TMP_FILE"
# Get original size
SIZE=$(cat "$TMP_FILE" | wc -c | awk '{comp=$1/1024; printf "%.2f", comp}')
@suhaotian
suhaotian / qs-stringify.lite.ts
Last active July 9, 2024 09:15
Lightweight `qs.stringify` implementation
/** Ref: https://github.com/suhaotian/xior/blob/main/src/utils.ts#L8 */
export function encodeParams<T = any>(
params: T,
encodeURI = true,
parentKey: string | null = null,
options?: {
allowDots?: boolean;
serializeDate?: (value: Date) => string;
arrayFormat?: 'indices' | 'repeat' | 'brackets';
@suhaotian
suhaotian / refresh-token.ts
Last active April 28, 2024 03:34
Automatically refreshing access token with fetch(xior)
import xior, { XiorError as AxiosError, XiorInstance, XiorResponse } from 'xior';
import errorRetry from 'xior/plugins/error-retry';
const http = xior.create();
http.plugins.use(
errorRetry({
enableRetry: (config, error) => {
if (error.response && shouldRefreshToken(error.response)) {
return true;
@suhaotian
suhaotian / lfs-auto-track.mjs
Created January 31, 2024 11:47
git lfs auto track
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
const cwd = process.cwd();
console.log(process.argv, cwd);
const files = process.argv.slice(3);
const conditions = process.argv[2];
@suhaotian
suhaotian / CaptchaWidget.tsx
Last active December 31, 2024 02:26
Cloudflare turnstile for next.js
'use client';
import Script from 'next/script';
import { useEffect, useRef, useState } from 'react';
const scriptLink = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
type TurnstileRenderParameters = Turnstile.RenderParameters;
export default function Captcha(
@suhaotian
suhaotian / Echart.tsx
Created November 17, 2021 08:57
echarts with typescript
import React, { useState, useRef, useEffect, useMemo, useLayoutEffect, useImperativeHandle, forwardRef, Ref } from 'react'
import echarts, { ECOption } from './echarts-ref';
export type EchartProp = {
option: ECOption,
style?: {
width: string,
height: string
}
className?: string
@suhaotian
suhaotian / bilibili-auto-answer.js
Created August 19, 2020 12:07
B站注册自动答题脚本
setInterval(() => {
[].slice
.call(document.querySelectorAll(".answer-outer"))
.forEach((item, i) => {
setTimeout(() => {
item.click();
}, 1500 * i);
});
}, 6 * 1000);
@suhaotian
suhaotian / floaty.tsx
Last active November 23, 2019 17:46
floating menu ball like iOS
import React, { useState, useRef } from 'react';
import { useSpring, animated } from 'react-spring';
import { useDrag } from 'react-use-gesture';
import { useWindowSize } from 'react-use';
function calcMinDirection(
x: number,
y: number,
w: number,
h: number,
{"_id":"5ca7693589cf3854f7eeb019","c":45,"domNodesVersion":22,"nodes":[{"_id":"5ca7693589cf38199feeb01a","tag":"body","classes":[],"children":["7d78c574-1e03-124f-3c24-dcfff1c895fb","1b33902b-afb7-4c69-56ea-cc27759a8221","1f485dcd-93c5-1c4b-acf7-c00daba17509"],"type":"Body"},{"_id":"7d78c574-1e03-124f-3c24-dcfff1c895fb","tag":"div","classes":["1c6a91d8-0dc2-a413-3dfe-af070a940403"],"children":["84c8acb6-06ec-a537-cdf9-78691902d127","899afc7a-862c-47f8-c987-3c447aae9332","2f7ac40f-af8f-5d37-0813-ec454e423745"],"data":{"tag":"div","grid":{"type":"container"}},"type":"Container"},{"_id":"84c8acb6-06ec-a537-cdf9-78691902d127","tag":"h1","classes":[],"children":["84c8acb6-06ec-a537-cdf9-78691902d128"],"data":{"tag":"h1","style":{"base":{"main":{"noPseudo":{"gridColumnStart":1,"gridColumnEnd":2,"gridRowStart":1,"gridRowEnd":2}}}}},"type":"Heading"},{"_id":"84c8acb6-06ec-a537-cdf9-78691902d128","text":true,"v":"Heading"},{"_id":"899afc7a-862c-47f8-c987-3c447aae9332","tag":"p","classes":[],"children":["899afc7a-862c-