Web
仅限浏览器环境的工具集,包含完整的文件下载策略、新窗口打开与 HTML class 切换
概述
Web 子模块通过独立的子路径 @skyroc/utils/web 导出,与主入口分离,避免在非浏览器环境(Node.js、SSR)引入 BOM API。
import { downloadFileFromUrl, openWindow, toggleHtmlClass } from '@skyroc/utils/web';包含三个职责:
| 模块 | 导出 | 说明 |
|---|---|---|
download | downloadFileFromUrl 等 7 个函数 | 多策略文件下载 |
window | openWindow | 安全地在新窗口打开 URL |
class | toggleHtmlClass | 切换 <html> 元素上的 class |
文件下载
下载场景的难点在于:不同来源(URL / Base64 / Blob)、不同平台(iOS / 桌面 / CORS 限制)需要不同的处理策略。download 模块将这些复杂性封装在内部,对外提供清晰的函数边界。
选择哪个函数?
文件来源是什么?
├─ 普通 URL(http/https) → downloadFileFromUrl
├─ 图片 URL(需转 base64) → downloadFileFromImageUrl
├─ Base64 / DataURL → downloadFileFromBase64
├─ Blob 对象 → downloadFileFromBlob
├─ BlobPart(ArrayBuffer 等)→ downloadFileFromBlobPart
└─ 自定义 href → triggerDownload(底层)downloadFileFromUrl(options) — 异步
通过 URL 下载文件,内置跨平台兼容逻辑:
- iOS / iPadOS → 直接
openWindow(a[download]在 iOS 上不可靠) - 桌面端,CORS 允许 →
fetch → blob → a[download](最稳定,可自定义文件名) - 桌面端,CORS 不允许 → 回退
openWindow
文件名解析优先级:Content-Disposition 响应头 → 参数 fileName → URL 路径中的文件名。
import { downloadFileFromUrl } from '@skyroc/utils/web';
// 最简用法
await downloadFileFromUrl({ source: 'https://example.com/report.pdf' });
// 指定文件名
await downloadFileFromUrl({
source: 'https://example.com/export?id=123',
fileName: '月度报表.xlsx',
});
// 在当前 tab 打开(适合预览)
await downloadFileFromUrl({
source: 'https://example.com/preview.pdf',
target: '_self',
});interface DownloadOptions {
source: string; // 文件 URL
fileName?: string; // 自定义文件名(可选)
target?: string; // 回退打开窗口的 target,默认 '_blank'
}downloadFileFromBase64(options)
通过 Base64 / DataURL 下载文件,同步。
import { downloadFileFromBase64 } from '@skyroc/utils/web';
downloadFileFromBase64({
source: 'data:application/pdf;base64,JVBERi0x...',
fileName: 'document.pdf',
});
// 图片(省略 mimeType 前缀也可以,但推荐带完整 DataURL)
downloadFileFromBase64({
source: 'data:image/png;base64,iVBORw0KGgo...',
fileName: 'screenshot.png',
});downloadFileFromImageUrl(options) — 异步
通过图片 URL 下载图片,内部用 Canvas 将图片转为 Base64 后下载。
跨域限制: 图片服务端必须返回
Access-Control-Allow-Origin响应头,否则 Canvas 会被"污染"而抛错。
import { downloadFileFromImageUrl } from '@skyroc/utils/web';
await downloadFileFromImageUrl({
source: 'https://cdn.example.com/avatar.png',
fileName: 'my-avatar.png',
});downloadFileFromBlob(options)
通过 Blob 对象下载,同步。适合服务端返回的二进制响应。
import { downloadFileFromBlob } from '@skyroc/utils/web';
// 配合 axios(responseType: 'blob')
const response = await axios.get('/api/export', { responseType: 'blob' });
downloadFileFromBlob({
source: response.data, // Blob
fileName: '数据导出.xlsx',
});downloadFileFromBlobPart(options)
通过 BlobPart(string | ArrayBuffer | Uint8Array 等)下载,同步。适合需要自行构造文件内容的场景。
import { downloadFileFromBlobPart } from '@skyroc/utils/web';
const csvContent = 'name,age\nAlice,30\nBob,25';
downloadFileFromBlobPart({
source: csvContent,
fileName: 'users.csv',
});urlToBase64(url, mimeType?) — 异步
将图片 URL 转为 Base64 DataURL(底层实现)。跨域图片需要服务端允许 CORS。
import { urlToBase64 } from '@skyroc/utils/web';
const base64 = await urlToBase64('https://cdn.example.com/image.png');
// 'data:image/png;base64,...'triggerDownload(href, fileName, revokeDelay?) — 底层
通用下载触发函数,通过动态创建 <a> 标签并模拟点击实现下载。是其他 download* 函数的内部实现。
一般不需要直接调用,除非需要传入已有的 blob URL 或 data URL:
import { triggerDownload } from '@skyroc/utils/web';
const blobUrl = URL.createObjectURL(myBlob);
triggerDownload(blobUrl, 'file.zip');
// blob URL 会在 150ms 后自动 revokeObjectURLopenWindow
import { openWindow } from '@skyroc/utils/web';在新窗口(或指定 target)打开一个 URL,默认启用 noopener,noreferrer 安全策略防止 opener 劫持。
// 新 tab 打开(默认)
openWindow('https://docs.example.com');
// 当前 tab
openWindow('/settings', { target: '_self' });
// 关闭安全策略(需要 opener 引用时)
openWindow('https://external.com', { secure: false });interface OpenWindowOptions {
target?: '_blank' | '_parent' | '_self' | '_top' | string; // 默认 '_blank'
secure?: boolean; // 默认 true,开启 noopener + noreferrer
}toggleHtmlClass
import { toggleHtmlClass } from '@skyroc/utils/web';切换 document.documentElement(即 <html> 标签)上的 class,常用于主题切换(暗色模式)。
const darkMode = toggleHtmlClass('dark');
darkMode.add(); // <html class="dark">
darkMode.remove(); // <html class="">在 React 中配合主题状态使用:
import { useEffect } from 'react';
import { toggleHtmlClass } from '@skyroc/utils/web';
const dark = toggleHtmlClass('dark');
const ThemeWatcher = ({ isDark }: { isDark: boolean }) => {
useEffect(() => {
isDark ? dark.add() : dark.remove();
}, [isDark]);
return null;
};