Http请求
URL解析
常用取值方式
ts
// 完整 URL
window.location.href // "https://www.example.com/path/page?id=123&name=test#section"
// 拆分各部分
window.location.protocol // "https:"
window.location.host // "www.example.com" (域名+端口)
window.location.hostname // "www.example.com" (仅域名)
window.location.port // "8080" (端口号,默认端口返回空字符串)
window.location.pathname // "/path/page" (路径)
window.location.search // "?id=123&name=test" (查询参数,含 ?)
window.location.hash // "#section" (锚点,含 #)解析查询参数
不要手动 split search,直接用原生 API
| 方法 | 作用 | 返回值 |
|---|---|---|
get(name) | 获取第一个值 | string | null |
getAll(name) | 获取所有同名值 | string[] |
has(name) | 判断参数是否存在 | boolean |
set(name, value) | 设置/覆盖(去重) | void |
append(name, value) | 追加(保留多个同名) | void |
delete(name) | 删除参数 | void |
toString() | 序列化为查询字符串(不含?) | string |
示例
new URLSearchParams()
ts
const params = new URLSearchParams(window.location.search);
params.get('id') // "123"
params.get('name') // "test"
params.has('token') // true / false
params.getAll('tag') // ["a", "b"] (同名参数多个值)new URL()
ts
const url = new URL('https://example.com/list?page=2&sort=name&tag=js&tag=ts');
url.searchParams.get('page'); // "2"
url.searchParams.get('sort'); // "name"Cookie 增删改查封装
ts
const CookieUtil = {
/**
* 增加/修改cookie
* @param {string} name 属性名
* @param {string} value 属性值
* @param {object} options 可选参数,包含过期时间、路径、是否安全、是否HttpOnly、SameSite策略
* @param {number} options.days 过期时间,单位为天,默认为1天
* @param {string} options.Path cookie的路径,默认为'/'
* @param {boolean} options.Secure 是否为安全cookie,默认为false
* @param {boolean} options.HttpOnly 是否为HttpOnly cookie,默认为false
* @param {string} options.SameSite SameSite策略,可选值为'Lax'、'Strict'、'None',默认为'Lax'
*/
set(name: string, value: string, { days = 1, Path = '/', Secure = false, HttpOnly = false, SameSite = 'Lax'} = {}): void {
// 计算过期时间,单位为秒
const maxAge = days * 24 * 60 * 60;
// 设置cookie,使用 encodeURIComponent 对属性值进行编码,防止特殊字符导致的错误,path=/表示在整个域名下都可访问
document.cookie = `${name}=${encodeURIComponent(value)}; Max-Age=${maxAge}; Path=${Path}; ${Secure ? 'Secure; ' : ''}${HttpOnly ? 'HttpOnly; ' : ''}SameSite=${SameSite};`;
},
/**
* 获取cookie
* @param {string} name 属性名
* @returns {string|null} 属性值,不存在时返回 null
*/
get(name: string): string | null {
// 使用正则表达式匹配 cookie 中的指定属性名,并获取其对应的属性值
const match = document.cookie.match(new RegExp('(?:^|;\\s*)' + name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '=([^;]*)'));
// 如果匹配成功,返回解码后的属性值,否则返回 null
return match ? decodeURIComponent(match[1]) : null;
},
/**
* 删除cookie
* @param {string} name 属性名
* @param {object} options 可选参数,包含cookie的路径
* @param {string} options.Path cookie的路径,默认为'/'
*/
remove(name: string, { Path = '/' } = {}): void {
// 设为-1天,立即过期
document.cookie = `${name}=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=${Path}`;
},
/**
* 一键删除指定路径下的所有cookie,无法删除 HttpOnly 和子域 cookie
* @param {string} Path cookie的路径,默认为'/'
*/
clearAll(Path = '/'): void {
// 无cookie时直接跳过
if (!document.cookie) return;
// 使用 forEach 遍历 document.cookie.split(';'),对每个 cookie 字符串进行处理
document.cookie.split(';').forEach(cookie => {
// 分割出key
const name = cookie.split('=')[0].trim();
// key存在直接删除
name && this.remove(name, { Path });
});
}
};fetch请求【可取消上一个请求】
ts
// 用于记录上一个http请求【当有新请求时,可用于取消上一个http请求】
let currentController: AbortController | undefined;
const http = async () => {
// 每次回调时重新创建控制器,确保 signal 是全新的
const controller = new AbortController();
// 信号
const signal = controller.signal;
// 如果上一个请求未完成,则直接取消
currentController && currentController.abort();
// 把最新的请求存储到外部,方便清理
currentController = controller;
try {
// 发送请求
const sendHttp = await fetch(`https://api.xxx-xxx-xxx.com/API`, {
method: 'POST',
signal: signal, // 绑定取消信号
headers: {
'Content-Type': 'application/json' // 告诉服务器发送的是 JSON 数据
},
body: JSON.stringify({
// Parse: ''
}) // 【关键】将数据转换为 JSON 字符串放入请求体
});
// 转换json数据
const httpResult = await sendHttp.json();
// 后续业务逻辑
} catch (error: any) {
// error.name='AbortError' 表示主动取消,不需要提醒
if(error.name === 'AbortError') return;
// 后续错误处理
}
};fetch上传文件、图片【多个】
ts
// 用于记录上一个http请求【当有新请求时,可用于取消上一个http请求】
let currentController: AbortController | undefined;
const uploadImageorFile = async (files: Array<File>) => {
// 每次回调时重新创建控制器,确保 signal 是全新的
const controller = new AbortController();
// 信号
const signal = controller.signal;
// 如果上一个请求未完成,则直接取消
currentController && currentController.abort();
// 把最新的请求存储到外部,方便清理
currentController = controller;
// 构建 FormData
const formData = new FormData();
// 遍历所有选中的文件
for (let i = 0; i < files.length; i++) {
// 【files】为后端接收的字段名称
formData.append('files', files[i]!);
/** 参数1 (参数名/字段名) ;参数2:value (值/内容);参数3:filename (文件名) —— 可选 **/
// formData.append(name, blob, 'filename.txt'); 【也可以传blob】
}
try {
// 发送请求
const sendHttp = await fetch('https://api.xxx.com/upload', {
// 请求模式
method: 'POST',
// 绑定取消信号
signal: signal,
// 请求体
body: formData,
// 请求头配置
headers: {
// 不要手动设置 Content-Type!【FormData 作为 body 时,浏览器会自动设置:】
}
});
// 当 HTTP 状态码在 200~299 之间时,ok 为 true
// 其他情况(如 404、500)都是 false
// 作用:抛出一个错误,让代码跳到 catch 里
if (!sendHttp.ok) throw new Error(`HTTP error! status: ${sendHttp.status}`);
// 转换json数据
const httpResult = await sendHttp.json();
// 后续业务逻辑
} catch (error: any) {
// error.name='AbortError' 表示主动取消,不需要提醒
if(error.name === 'AbortError') return;
// 后续错误处理
}
};