JavaScript API
PostMessage
- 同源;
- 跨 window 通信;
PostMessage API
- Window.prototype.postMessage(message: any, targetOrigin: string, transfer?: Transferable[]): void 实例方法: 发送消息至目标窗口, targetOrigin 为目标窗口源;
- Window.prototype.addEventListener("message", (event: MessageEvent) => void): void 实例方法: 监听跨上下文消息事件;
- MessageEvent.prototype.origin: string 实例属性: 发送者的源;
- MessageEvent.prototype.data: any 实例属性: 消息内容;
- MessageEvent.prototype.source: WindowProxy | null 实例属性: 发送者的 window 对象代理;
// 发送消息
let iframeWindow = document.getElementById("myframe").contentWindow;
iframeWindow.postMessage("A secret", "http://www.wrox.com");
// 接收消息
window.addEventListener("message", (event) => {
if (event.origin == "http://www.wrox.com") {
processMessage(event.data);
event.source.postMessage("Received!", "http://p2p.wrox.com");
}
});
二进制数据 API
Blob
- 二进制大对象;
Blob API
- Blob(blobParts?: BlobPart[], options?: BlobPropertyBag): Blob 构造函数: 创建二进制大对象;
- blobParts: 二进制数据数组, 可为 ArrayBuffer, ArrayBufferView, Blob, String...
- options: 二进制数据选项, 可为
{ type: MIME 类型 };
- Blob.prototype.size: number 实例属性: Blob 的字节大小;
- Blob.prototype.type: string 实例属性: Blob 的 MIME 类型;
Blob.prototype.arrayBuffer(): Promise<ArrayBuffer>实例方法: 返回 ArrayBuffer 格式的 Blob 内容;Blob.prototype.bytes(): AsyncIterable<number>实例方法: 返回字节迭代器;- Blob.prototype.slice(start?: number, end?: number, contentType?: string): Blob 实例方法: 返回 Blob 的子集;
- Blob.prototype.stream(): ReadableStream 实例方法: 返回 ReadableStream;
Blob.prototype.text(): Promise<string>实例方法: 返回字符串格式的 Blob 内容;
const obj = { hello: "world" };
const blob = new Blob([JSON.stringify(obj, null, 2)], {
type: "application/json",
});
const slice = blob.slice(0, 3);
File
- Blob 的封装;
- 专注于文件操作;
File API
- new File(fileBits, fileName, options): File 构造函数: 创建文件对象;
- fileBits: 文件内容数组, 可为 ArrayBuffer, ArrayBufferView, Blob, String...
- fileName: 文件名;
- options: 文件选项, 可为
{ type: MIME 类型, lastModified: 最后修改时间 };
var file = new File(["foo"], "foo.txt", {
type: "text/plain",
});
FileReader
- 异步读取 Blob/File;
- 创建 FileReader 对象;
FileReader API
- FileReader(): FileReader 构造函数: 异步读取 Blob/File;
- FileReader.prototype.error: DOMException | null 实例属性: 读取错误时存储错误信息;
- FileReader.prototype.readyState: number 实例属性: 读取状态 (0=EMPTY, 1=LOADING, 2=DONE);
- FileReader.prototype.result: string | ArrayBuffer | null 实例属性: 读取结果;
- FileReader.prototype.abort(): void 实例方法: 中止读取操作;
- FileReader.prototype.readAsArrayBuffer(blob: Blob): void 实例方法: 将 Blob 读取为 ArrayBuffer;
- FileReader.prototype.readAsDataURL(blob: Blob): void 实例方法: 将 Blob 读取为 Data URL;
- FileReader.prototype.readAsText(blob: Blob, encoding?: string): void 实例方法: 将 Blob 读取为文本;
- FileReader.prototype.onabort: ((this: FileReader, ev: Event) => void) | null 实例属性: 中止读取时触发;
FileReader.prototype.onerror: ((this: FileReader, ev: ProgressEvent<FileReader>) => void) | null实例属性: 读取错误时触发;FileReader.prototype.onload: ((this: FileReader, ev: ProgressEvent<FileReader>) => void) | null实例属性: 读取成功时触发;FileReader.prototype.onloadstart: ((this: FileReader, ev: ProgressEvent<FileReader>) => void) | null实例属性: 开始读取时触发;FileReader.prototype.onprogress: ((this: FileReader, ev: ProgressEvent<FileReader>) => void) | null实例属性: 读取过程中触发 (每 50ms);
const reader = new FileReader();
// 读取文件为 arraybuffer
reader.readAsArrayBuffer(file);
const content = reader.result;
// 停止读取文件
reader.abort(); // 触发 abort 事件
// 监听 progress 事件
reader.addEventListener(
"progress",
() => {
//...
},
false,
);
Object URL
- 使用 text, base64...编码 Blob 对象;
Object URL API
- window.URL.createObjectURL(blob): 接受一个 File 或 Blob 对象;
- window.URL.revokeObjectURL(url): 释放一个之前创建的 Object URL;
url = window.URL.createObjectURL(file);
img.src = url;
window.URL.revokeObjectURL(url);
Base64 编码
- 基于 64 个可打印字符来表示二进制数据;
Base64 API
- window.atob(base64String): string 全局方法: 将 base64 编码字符串解码为普通字符串;
- window.btoa(string): string 全局方法: 将普通字符串编码为 base64 字符串;
Data Url
- 数据 URL 是一种特殊的 URL, 使用 Base64 编码表示数据;
- 格式:
data:[<mediatype>][;base64],<data>;
Data Url API
- FileReader.prototype.readAsDataURL(blob: Blob): void 实例方法: 将 Blob 读取为 Data URL;
- HTMLCanvasElement.prototype.toDataURL(type?: string, quality?: number): string 实例方法: 将 Canvas 内容转换为 Data URL;
原生拖放
拖放事件
拖动触发顺序
- 触发元素为拖动元素;
- dragstart, drag, dragend 依次触发;
- 开始移动鼠标瞬间触发 dragstart 事件;
- 拖动鼠标持续触发 drag;
- 停止移动触发 dragend 事件;
放置触发顺序
- 触发元素为目标放置元素;
- dragenter, dragover, drop/dragleave 依次触发;
- 移动到放置目标上触发 dragenter 事件;
- dragenter 触发后, 拖动元素只要在放置元素范围内持续触发 dragover 事件;
- 离开放置元素范围触发 dragleave 事件;
- 放置到目标元素后触发 drop 事件;
DataTransfer
- 暴露于拖放回调中 event 属性;
- 用于被拖动元素向放置目标传递字符串数据;
DataTransfer API
- DataTransfer.prototype.dropEffect: "none" | "copy" | "link" | "move" 实例属性: 放置效果类型;
- DataTransfer.prototype.effectAllowed: "none" | "copy" | "copyLink" | "copyMove" | "link" | "linkMove" | "move" | "all" | "uninitialized" 实例属性: 允许的拖动效果;
- DataTransfer.prototype.files: FileList 实例属性: 拖动的文件列表;
- DataTransfer.prototype.items: DataTransferItemList 实例属性: 拖动的数据项列表;
- DataTransfer.prototype.types: string[] 实例属性: 拖动数据的类型列表;
- DataTransfer.prototype.clearData(format?: string): void 实例方法: 清除指定格式的数据;
- DataTransfer.prototype.getData(format: string): string 实例方法: 获取指定格式的数据;
- DataTransfer.prototype.setData(format: string, data: string): void 实例方法: 设置指定格式的数据;
- DataTransfer.prototype.setDragImage(image: Element, xOffset: number, yOffset: number): void 实例方法: 设置自定义拖动图像;
可拖动能力
可拖动能力
- 图片, 链接和文本默认可拖动;
- 可使用 draggable 设置任意元素可拖动;
<!-- 禁止拖动图片 -->
<img src="smile.gif" draggable="false" alt="Smiley face" />
<!-- 让元素可以拖动 -->
<div draggable="true">...</div>
强制放置
- 部分元素不支持放置, 可通过覆盖 dragover 和 dragenter 默认行为将任何标签转换为可放置目标;
let droptarget = document.getElementById("droptarget");
droptarget.addEventListener("dragover", (event) => {
event.preventDefault();
});
droptarget.addEventListener("dragenter", (event) => {
event.preventDefault();
});
Notifications
Notifications API
- Notification(title: string, options?: NotificationOptions): Notification 构造函数: 创建并显示通知;
- Notification.prototype.close(): void 实例方法: 隐藏通知;
- Notification.prototype.onshow: ((this: Notification, ev: Event) => void) | null 实例属性: 显示通知时触发;
- Notification.prototype.onclick: ((this: Notification, ev: Event) => void) | null 实例属性: 点击通知时触发;
- Notification.prototype.onclose: ((this: Notification, ev: Event) => void) | null 实例属性: 关闭通知时触发;
- Notification.prototype.onerror: ((this: Notification, ev: Event) => void) | null 实例属性: 通知错误时触发;
const n = new Notification("Title text!");
setTimeout(() => n.close(), 1000);
n.onshow = () => console.log("Notification was shown!");
n.onclose = () => console.log("Notification was closed!");
通知权限
- 只能在安全上下文触发;
- 每个源必须得到用户允许;
- 权限请求每个域只能触发一次;
Notification.requestPermission(): Promise<"granted" | "denied" | "default">实例方法: 返回当前源的通知权限;
Notification.requestPermission().then((permission) => {
console.log("User responded to permission request:", permission);
});
Page Visibility
Page Visibility API
- Document.prototype.visibilityState: "visible" | "hidden" | "prerender" | "unloaded" 实例属性: 页面当前状态;
- Document.prototype.hidden: boolean 实例属性: 页面是否隐藏;
- Document.prototype.addEventListener("visibilitychange", (event: Event) => void): void 实例方法: 监听页面可见性变化事件;
const visibilityState = document.visibilityState;
document.addEventListener("visibilitychange", () => {
// ...
});
Performance
Performance
- 当前页面的性能信息, 高精度数据;
Performance API
- Performance.prototype.now(): number 实例方法: 返回微秒级别的浮点值, 从执行上下文创建计时;
- Performance.prototype.timeOrigin: number 实例属性: 执行上下文创建的基准值;
- Performance.prototype.eventCounts: EventCounts 实例属性: 事件计数器;
- Performance.prototype.navigation: PerformanceNavigation 实例属性: 导航性能信息;
- Performance.prototype.timing: PerformanceTiming 实例属性: 导航时间信息;
- Performance.prototype.clearMarks(markName?: string): void 实例方法: 清除指定标记;
- Performance.prototype.clearMeasures(measureName?: string): void 实例方法: 清除指定测量;
- Performance.prototype.clearResourceTimings(): void 实例方法: 清除资源时间;
- Performance.prototype.getEntries(): PerformanceEntryList 实例方法: 获取所有性能条目;
- Performance.prototype.getEntriesByName(name: string, entryType?: string): PerformanceEntryList 实例方法: 根据名称获取性能条目;
- Performance.prototype.getEntriesByType(type: string): PerformanceEntryList 实例方法: 根据类型获取性能条目;
- Performance.prototype.mark(markName: string): PerformanceMark 实例方法: 创建标记;
- Performance.prototype.measure(measureName: string, startMark?: string, endMark?: string): PerformanceMeasure 实例方法: 创建测量;
- Performance.prototype.setResourceTimingBufferSize(maxSize: number): void 实例方法: 设置资源时间缓冲区大小;
- Performance.prototype.toJSON(): any 实例方法: 返回 JSON 格式的性能数据;
- PerformanceEntry.prototype.name: string 实例属性: 性能条目名称;
- PerformanceEntry.prototype.entryType: string 实例属性: 性能条目类型;
- PerformanceEntry.prototype.startTime: number 实例属性: 性能条目开始时间;
- PerformanceEntry.prototype.duration: number 实例属性: 性能条目持续时间;
Web Cryptography
随机数 API
Crypto.prototype.getRandomValues<T extends Uint8Array | Uint16Array | Uint32Array | Int8Array | Int16Array | Int32Array | Float32Array | Float64Array>(array: T): T实例方法: 生成指定类型数组对应位数的随机数;- Crypto.prototype.randomUUID(): string 实例方法: 生成 UUID;
const array = new Uint8Array(1);
const fooArray = new Uint32Array(1);
for (let i = 0; i < 5; ++i) {
console.log(crypto.getRandomValues(array)); // 产生 5 个 8 位随机数
console.log(crypto.getRandomValues(fooArray)); // 产生 5 个 32 位随机数
}
let uuid = crypto.randomUUID();
最佳实践
二进制数据的相互转换
Data URL <=> [atob/btoa] <=> base64 <=> Binary String <=> [自己拼] <=> TypeArray <=> ArrayBuffer <=> [TextDecoder/Encoder] <=> Text;ArrayBuffer/Text <=> Blob => File => [FileReader] => Data URL/ArrayBuffer/Text;TypeArray <=> ArrayBuffer <=> Blob/File => Object URL;
大文件上传
分片上传
- 根据一定规则, 将大文件分割成若干片;
- 客户端发送分片规则, 每个分片具有唯一标识;
- 串行或者并行发送各分片;
- 所有分片发送完毕后, 服务端根据 md5 判断数据是否上传完整, 完整则合并分片为原始文件;
第一个分片
- 第一个分片附带原始文件 md5, 用于服务器验证文件完整性检验;
- 第一个分片大小最好小;
断点续传
- 客户端传送给服务器端分片信息;
- 服务器端接受分片, 保存为临时文件, 根据分片信息判断上传进度;
- 如果发生网络错误, 恢复连接后, 服务器端发送给客户端当前仍未发送的分片信息;
- 客户端继续发送剩余分片;