实用指南:前端 20 个零依赖浏览器原生 API 实战清单

1. ResizeObserver

ResizeObserver 用于监听元素的尺寸变化

简单来说:它能在元素的宽高变化时(包括内容撑开、CSS 改变、父容器变化等)自动触发回调。

1.1. 语法

const observer = new ResizeObserver(entries => {
  for (let entry of entries) {
    console.log('元素尺寸变化了:')
    console.log('contentRect:', entry.contentRect)
    console.log('宽度:', entry.contentRect.width)
    console.log('高度:', entry.contentRect.height)
  }
})
observer.observe(document.querySelector('.box'))

参数说明:

  • entries:一个数组,每个元素是一个观察对象(ResizeObserverEntry)。
  • entry.contentRect:包含目标元素的内容区域大小。
  • observer.observe(el):开始观察某个元素。
  • observer.unobserve(el):停止观察某个元素。
  • observer.disconnect():断开所有观察。

1.2. 代码演示



  
    
    ResizeObserver Demo
    
  
  
    
拖拽我改变大小
<script> const box = document.querySelector('.box') const observer = new ResizeObserver(entries => { for (let entry of entries) { console.log('新尺寸:', entry.contentRect.width, entry.contentRect.height) } }) observer.observe(box) </script>

1.3. 使用场景

  1. 自适应布局:元素大小变化时动态调整字体、图片尺寸等
  2. 图表组件:容器大小变化时重新渲染 Echarts、D3 图
  3. 动态内容:监控富文本内容区域变化触发滚动或对齐逻辑
  4. 虚拟滚动:当容器高度变化时重新计算可视区域范围
  5. 动画效果:元素尺寸发生变化触发动画或者过度效果

2. IntersectionObserver

IntersectionObserver 用来 监听元素是否进入(或离开)可视区域

它可以在不监听滚动事件、不计算位置的情况下,高效检测元素是否出现在视口中

2.1. 语法

const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('元素进入视口!', entry.target)
    } else {
      console.log('元素离开视口!', entry.target)
    }
  })
}, {
  root: null, // 观察的滚动容器,null 表示视口
  threshold: 0.1 // 触发回调的阈值(可见比例)
})
// 开始观察目标元素
observer.observe(document.querySelector('.box'))

参数说明:

  • entries:被观察元素的信息列表
  • entry.isIntersecting:元素是否进入可视区
  • entry.intersectionRatio:元素可见比例(0~1)
  • entry.target:当前被观察的 DOM 元素
  • root:可滚动容器(默认为浏览器视口)
  • threshold:元素可见比例达到多少触发(如 0.5 表示一半可见)

2.2. 代码演示



  
    
    IntersectionObserver Demo
    
  
  
    
向下滚动
观察我
<script> const box = document.querySelector(".box"); const observer = new IntersectionObserver( entries => { entries.forEach(entry => { if (entry.isIntersecting) { box.classList.add("visible"); console.log("进入可视区"); } else { box.classList.remove("visible"); console.log("离开可视区"); } }); }, { threshold: 0.2, // 元素有20%可见时触发 } ); observer.observe(box); </script>

2.3. 使用场景

  1. 图片懒加载:只有当图片进入视口时才加载真正的图片
  2. 无限滚动加载:页面滚动到底部时加载新数据
  3. 曝光埋点:广告或模块进入视口时统计曝光次数
  4. 动画触发:元素进入视口时触发淡入或淡出效果
  5. 页面定位:滚动时判断当前在哪个内容段落

3. Page Visibility

Page Visibility 用于判断网页当前是否在用户可见的状态

比如:

  • 用户切换到了其他页面
  • 用户最小化浏览器
  • 用户返回后重新看到当前页面

3.1. 语法

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    console.log('页面隐藏了!')
  } else {
    console.log('页面可见了!')
  }
})

取值如下:

  • visible:页面当前可见(在前台)
  • hidden:页面不可见(被最小化或切换到其他标签页)
  • prerender:页面正在预渲染。

3.2. 代码演示



  
    
    Page Visibility Demo
  
  
    

切换标签页看看控制台输出

<script> document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') { console.log('页面隐藏了!') } else { console.log('页面可见了!') } }) </script>

3.3. 使用场景

  1. 视频播放优化:页面隐藏时暂停视频,返回后恢复播放
  2. 定时任务暂停:页面隐藏时暂停 setInterval 计时器
  3. 数据上报节省资源:页面隐藏时停止心跳或轮询请求
  4. 游戏性能优化:页面暂停时暂停动画、音效等逻辑
  5. 节能优化:减少 CPU 和内存消耗

4. Web Share

Web Share API 允许网页使用原生的分享界面(通常是手机系统的原生分享面板),用户可以把网页内容分享到其他应用。

简单来说:它让网页像 App 一样,可以“调用系统分享功能”。

4.1. 语法

if (navigator.share) {
  navigator.share({
    title: '我的网站标题',
    text: '来看看这个网站!',
    url: 'https://example.com'
  })
    .then(() => console.log('分享成功'))
    .catch((err) => console.error('分享失败:', err));
} else {
  console.warn('当前浏览器不支持 Web Share API');
}

navigator.share() 的参数是一个对象,可以包含以下字段:

字段

类型

说明

title

string

分享标题

text

string

分享文本

url

string

要分享的链接

files

File[]

可选,支持分享图片/视频文件(需HTTPS)

4.2. 代码演示



  
    
    
    原生 Web Share API 示例
  
  
    
    <script>
      const shareBtn = document.getElementById("shareBtn");
      shareBtn.addEventListener("click", async () => {
        if (navigator.share) {
          try {
            await navigator.share({
              title: document.title,
              text: "快来看看这个有趣的页面!",
              url: window.location.href,
            });
            console.log("用户已完成分享");
          } catch (err) {
            console.error("用户取消或分享失败:", err);
          }
        } else {
          alert("当前浏览器不支持 Web Share API");
        }
      });
    </script>
  

5. Wake Lock

Wake Lock API(唤醒锁)可以防止设备因为“无操作”而自动关闭屏幕或进入休眠。目前主要支持屏幕唤醒锁(screen)。

5.1. 代码演示



  
    
    
    Wake Lock Demo
  
  
    

Wake Lock API 示例

<script> let wakeLock = null; // 请求唤醒锁 async function requestWakeLock() { try { wakeLock = await navigator.wakeLock.request("screen"); console.log("屏幕唤醒锁已启用"); wakeLock.addEventListener("release", () => { console.log("唤醒锁已被释放"); }); } catch (err) { console.error("请求唤醒锁失败:", err); } } // 释放唤醒锁 function releaseWakeLock() { if (wakeLock) { wakeLock.release(); wakeLock = null; } } // 页面可见性变化时自动恢复锁(浏览器切出会自动失效) document.addEventListener("visibilitychange", () => { if (wakeLock !== null && document.visibilityState === "visible") { requestWakeLock(); } }); // 绑定按钮 document.getElementById("lockBtn").addEventListener("click", requestWakeLock); document.getElementById("unlockBtn").addEventListener("click", releaseWakeLock); </script>

讲解:

  • navigator.wakeLock.request('screen'):申请一个保持屏幕唤醒的锁
  • wakeLock.release():主动释放锁
  • wakeLock.addEventListener('release', …):监听系统自动释放锁(比如切换标签页)
  • document.visibilitychange:当页面重新可见时重新请求锁(防止切出去回来实效)

5.2. 注意事项

  1. 只在 HTTPS 环境可用(或 localhost)
  2. 多数现代浏览器(如 Chrome、Edge、Android WebView)支持,IOS Safari 目前支持较差
  3. 必须在用户交互(点击按钮触发),否则会报错

5.3. 应用场景

  • 视频播放页面(防止看视频的时候屏幕黑掉)
  • 阅读器/在线文档
  • 导航类网页
  • 运动、计时类 Web App

6. BroadcastChannel

BroadcastChannel 允许在多个浏览器上下文(tab、iframe、web worker)直接进行消息广播。所有使用相同频道名(channel name)的页面都能互相通信

简单来说:它就像浏览器内的“广播电台”——所有收听同一个频道名的页面都能收到同样的消息。

6.1. 代码演示



  
    
    BroadcastChannel Demo
  
  
    

BroadcastChannel 示例

<script> // 1️. 创建频道(同名频道可以互相通信) const channel = new BroadcastChannel("chat-channel"); const log = document.getElementById("log"); // 2️. 接收来自其他页面的消息 channel.onmessage = event => { const msg = event.data; log.innerHTML += `

收到消息:${msg}

`; }; // 3️. 输入框发送消息 document.getElementById("msgInput").addEventListener("change", e => { const message = e.target.value; channel.postMessage(message); // 广播给所有订阅此频道的页面 e.target.value = ""; }); </script>

API 详解

  • new BroadcastChannel(name):创建一个频道对象,参数时频道名(字符串)
  • channel.postMessage(data):向频道广播一条消息
  • channel.onmessage = handler:监听频道收到的消息
  • channel.close():关闭当前频道

6.2. 特点与限制

特点

说明

实时通信

同源标签页、iframe、Web Worker 均可共享

同源限制

只能在相同协议 + 域名 + 端口的上下文间通信

不能跨浏览器

只在当前浏览器进程有效(不同浏览器或隐私模式之间不共享)

生命周期短

页面关闭或 close()

后会自动失效

6.3. 应用场景

  1. 多标签页同步登陆状态:登陆或退出后,其他标签自动同步状态
  2. 多窗口聊天::多页面共享聊天内容
  3. 多标签页操作同步:设置项、主题切换实时同步
  4. 与 Web Worker 通信:主线程和 Worker 间广播状态变化

7. PerformanceObserver

PerformanceObserver用于监听性能事件(performance entries),这些是浏览器在运行过程中自动记录的,比如:

  • 页面加载阶段的指标(如 navigation)
  • 资源加载(如 resource)
  • 长任务(如 longtask)
  • 渲染阶段(如 paint)

简单理解:它是浏览器提供的性能监听器,可以实时获取数据而不需要手动打断点

7.1. 语法

// 创建一个性能观察者
const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries()
  for (const entry of entries) {
    console.log('性能事件:', entry)
  }
})
// 监听的类型,比如资源加载、页面导航等
observer.observe({ type: 'resource', buffered: true })

7.2. 常见可观察类型

类型名

说明

示例字段

'resource'

外部资源加载性能(CSS、JS、图片等)

entry.namedurationinitiatorType

'navigation'

页面加载性能(HTML 解析、DNS、TCP、DOM 等)

domContentLoadedEventEndloadEventEnd

'paint'

渲染阶段性能(首次绘制 FP、首次内容绘制 FCP)

entry.name = 'first-paint' / 'first-contentful-paint'

'longtask'

长任务检测(主线程卡顿 >50ms)

entry.duration

'largest-contentful-paint'

最大内容绘制(LCP)

用于 Core Web Vitals

'layout-shift'

布局偏移(CLS)

entry.value 表示偏移量

7.3. 代码演示



  
    
    PerformanceObserver Demo
  
  
    

性能观察 Demo

<script> // 监听页面加载性能 const perfObserver = new PerformanceObserver((list) => { list.getEntries().forEach(entry => { console.log(`[${entry.entryType}]`, entry.name, entry.duration) }) }) perfObserver.observe({ type: 'resource', buffered: true }) // 资源加载 perfObserver.observe({ type: 'paint', buffered: true }) // 首次绘制 perfObserver.observe({ type: 'navigation', buffered: true }) // 页面加载 </script>

7.4. 应用场景

  1. 页面加载性能监控:统计首屏加载时间、FCP、LCP
  2. 资源加载监控:判断哪些资源加载慢
  3. 性能上报:配合上报系统发送到后端分析
  4. 检测卡顿:使用‘longtask’监控主线程阻塞
  5. 用户体验分析:跟踪 CLS(内容抖动)、FID(首次交互延迟)

performance.getEntriesByType() 的区别?

performance.getEntriesByType('resource'):一次性获取当前已有的性能数据

performanceObserver:可以实时监听后续加载的性能数据(如懒加载图片)

前者是静态采样,后者是动态监听

8. requestIdleCallback

requestIdleCallback 会在浏览器“空闲”的时候调用你提供的回调函数。也就是说,当浏览器完成了渲染、用户交互、动画这些更重要的任务后,会在“有空”的时候来处理提交的低优先级任务。

8.1. 基本语法

const handle = requestIdleCallback(callback[, options])
cancelIdleCallback(handle)

参数解释:

  • callback(deadline):空闲时执行的函数,接收一个 deadline 参数
  • options.timeout:超时时间(如果太久没有空闲出来也强制执行)
requestIdleCallback(myTask, { timeout: 2000 })

deadline 对象解析

  • timeRemaining:返回当前帧剩余的空闲时间(毫秒)
  • dedTimeout:是否因超时被强制执行

8.2. 代码演示



  
    
    requestIdleCallback Demo
  
  
    

requestIdleCallback 示例

打开控制台,看看浏览器“空闲”时才执行的任务。

<script> const tasks = Array.from({ length: 10 }, (_, i) => `任务 ${i + 1}`) function processTasks(deadline) { while (deadline.timeRemaining() > 0 && tasks.length > 0) { const task = tasks.shift() console.log(`执行:${task}`) } if (tasks.length > 0) { // 还有任务没完成,继续安排下一个空闲回调 requestIdleCallback(processTasks) } else { console.log('所有任务完成') } } // 注册第一个空闲回调 requestIdleCallback(processTasks) </script>

运行效果:
浏览器在滚动、渲染、动画时不会执行任务,
当停止交互、主线程空闲时,控制台才逐个打印任务执行。

8.3. 使用场景

  1. 日志上报/埋点发送
  2. 缓存计算
  3. 非首屏数据预加载
  4. DOM 结构分析
  5. 离线缓存更新

9. scheduler.postTask

scheduler.postTask() 是一个 实验性的浏览器原生 API,属于 Scheduler API 的一部分,用于更智能、更精细地调度任务执行。
它比 setTimeoutrequestIdleCallback 更强大,也更精确地控制任务优先级。

window.scheduler.postTask() 允许开发者将任务加入浏览器的调度队列中,并通过指定优先级(priority)来控制执行顺序。浏览器会跟据空闲情况与优先级智能的调度任务。

9.1. 语法

scheduler.postTask(callback, options)

参数:

  • callback: 要执行的函数。
  • options: 一个配置对象,常用属性如下:
{
  priority: 'user-blocking' | 'user-visible' | 'background',
  signal: AbortSignal // 可选,用于取消任务
}
  • 返回值:返回一个 Promise 对象,在任务执行后 resolve。

9.2. 代码演示



  
    
    BroadcastChannel Demo
  
  
    
    <script>
      document.getElementById("btn").addEventListener("click", async () => {
        console.log("主线程任务开始");
        await scheduler.postTask(
          () => {
            console.log("高优先级任务:用户交互相关");
          },
          {priority: "user-blocking"}
        );
        await scheduler.postTask(
          () => {
            console.log("中优先级任务:界面更新");
          },
          {priority: "user-visible"}
        );
        await scheduler.postTask(
          () => {
            console.log("低优先级任务:后台统计");
          },
          {priority: "background"}
        );
        console.log("主线程任务结束");
      });
    </script>
  

9.3. 取消任务

const controller = new AbortController();
scheduler.postTask(() => {
  console.log('执行前被取消');
}, { signal: controller.signal });
controller.abort(); // 任务不会执行

9.4. 兼容性与区别

目前仅 Chrome 94+ / Edge 94+ / Opera 80+ 支持。
Safari 和 Firefox 仍未实现。

setTimeout:延迟执行任务。

requestIdleCallback:在浏览器空闲时执行。

scheduler.postTask:有浏览器智能调度,支持优先级

10. AbortController

AbrotController 用于创建一个取消信号(signal),这个信号可以被多个信号异步操作监听,当你调用 .abrot()时,这些操作会立即停止。

10.1. 语法

const controller = new AbortController();
const signal = controller.signal;
  • contoroller:控制器实例
  • signal:控制信号,用来传递“是否被取消”的状态

10.2. 代码演示



  
    
    AbortController 请求取消示例
  
  
    
    
    <script>
      let controller; // 保存控制器
      document.getElementById("start").addEventListener("click", () => {
        controller = new AbortController();
        const signal = controller.signal;
        console.log("开始发送请求...");
        // 这个接口会延迟 5 秒返回
        fetch("https://deelay.me/5000/https://jsonplaceholder.typicode.com/todos/1", {
          signal,
        })
          .then(res => res.json())
          .then(data => console.log("请求成功:", data))
          .catch(err => {
            if (err.name === "AbortError") {
              console.warn("请求被中止");
            } else {
              console.error("请求错误:", err);
            }
          });
      });
      document.getElementById("cancel").addEventListener("click", () => {
        if (controller) {
          controller.abort();
          console.log("已调用 abort()");
        }
      });
    </script>
  

11. ReadableStream

ReadableStream 是浏览器中强大的 原生流式 API(Streaming API),用于按块(chunk)读取数据,而不是一次性加载整个资源

简单来说 ReadableStream 能按块读取网络或文件内容,而不是等到所有内容都下载完

11.1. 为什么需要它

假设你要请求一个 1GB 的视频或超长文本:

  • 普通 fetch()axios 必须等到 整个响应加载完 才能处理。
  • 有了 ReadableStream,你可以 边下载边处理(比如流式渲染、实时显示日志、逐步解析 JSON 等)。

11.2. 基本语法

const reader = response.body.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  appendChunk(value);
}

概念:

  • ReadableStream:表示可读数据流(数据“源头”)
  • getReader():获取流的读取器
  • read():每次读取一块数据 { done, value }
  • controller.enqueue():向流中添加数据
  • controller.close():关闭流
  • TextDecoder:把二进制转为字符串

11.3. 代码演示:ai 流式输出



  
    
    
    AI 流式输出 Demo
    
  
  
    

AI 流式输出 Demo

<script> // --- 1. 获取 DOM 元素 --- const startButton = document.getElementById("startButton"); const outputElement = document.getElementById("ai-output"); // --- 2. 预先定义好 AI 的完整回答 --- const fullText = `当然!ReadableStream 是一个强大的 Web API,它允许你以数据块(chunks)的形式顺序地处理一个大数据源,而无需一次性将所有数据加载到内存中。 它的核心优势在于: 1. **内存高效**:无论源文件有多大,内存占用都极低。 2. **响应迅速**:一旦接收到第一个数据块,就可以立即开始处理,提升用户体验。 这对于处理大文件下载、流式音视频播放等场景至关重要。`; // --- 3. 模拟流式输出的函数 --- function simulateAIStream(text, element) { // 清空之前的内容 element.textContent = ""; // 将完整文本拆分成单个字符的数组 const chars = text.split(""); let charIndex = 0; // 设置一个定时器,每 50 毫秒追加一个字符 const streamInterval = setInterval(() => { // 检查是否还有字符需要显示 if (charIndex < chars.length) { // 追加一个字符到元素的 textContent 中 element.textContent += chars[charIndex]; charIndex++; } else { // 如果所有字符都已显示,清除定时器 clearInterval(streamInterval); console.log("流式输出完毕!"); } }, 30); // 30毫秒的间隔,可以调整这个值来改变打字速度 } // --- 4. 给按钮添加点击事件监听 --- startButton.addEventListener("click", () => { simulateAIStream(fullText, outputElement); }); </script>

11.4. 使用场景

  1. 大文件下载:边下载边显示进度
  2. AI/chatGPT 流式输出:模拟“字逐个出现”效果
  3. 实时日志流:实时打印服务器日志
  4. 流式 JSON 解析:大数据接口不必一次性解析

12. WritableStream

WritableStream 是用于以流的方式写入数据。简单来说它可以逐步接受并处理数据,而不是等整个一次性加载完。

WritableStream 是浏览器原生提供的一个“写入管道”,可以用来流式接收、处理或保存数据(而不是一次性加载完)。

一句话理解:

ReadableStream 是「读流」,
WritableStream 是「写流」。

当要把数据写入文件、网络请求、压缩器、转换器等目标时,就会用到WritableStream

12.1. 语法

const writer = stream.writable.getWriter();
await writer.write(chunk);

概念:

  • ReadableStream:“可读流” → 数据来源
  • WritableStream:“可写流” → 数据接收
  • pipeTo():将可读流连接到可写流
  • getWriter():获取写入对象
  • write:写入数据块
  • close():结束流
  • abrot():中断写入

12.2. 代码演示

const stream = new WritableStream({
  write(chunk) {
    console.log("写入数据:", chunk);
  },
  close() {
    console.log("写入完成");
  },
  abort(err) {
    console.error("写入被中止:", err);
  }
});
const writer = stream.getWriter();
writer.write("Hello");
writer.write("Stream");
writer.write("World");
writer.close();

12.3. 基本结构

new WritableStream({
  start(controller) {
    // 初始化时调用
  },
  write(chunk, controller) {
    // 每次写入调用
  },
  close(controller) {
    // 流关闭时调用
  },
  abort(reason) {
    // 流出错或被取消时调用
  }
})

12.4. 典型应用场景

  1. 文件写入:使用 File System Access API 进行本地保存
  2. 大文件上传:分块上传(比如大文件断点续传)
  3. 数据管道(pipe):与 ReadableStream 结合,实现“流式复制”
  4. 实时处理数据:边读边写,比如数据转换、压缩、加密
  5. 逐块写入磁盘或网络
  6. 实时保存草稿

12.5. 示例:从 ReadableStream 管道写入 WritableStream

const readable = new ReadableStream({
  start(controller) {
    ["A", "B", "C"].forEach(chunk => controller.enqueue(chunk));
    controller.close();
  },
});
const writable = new WritableStream({
  write(chunk) {
    console.log("接收到数据:", chunk);
  },
  close() {
    console.log("所有数据写入完成");
  },
});
// 管道连接:将 readable 的数据写入 writable
readable.pipeTo(writable);

12.6. 示例:写入文件(File System Access API)

async function saveFile() {
  // 1. 用户选择保存位置
  const fileHandle = await window.showSaveFilePicker({
    suggestedName: "stream-demo.txt"
  });
  const writable = await fileHandle.createWritable();
  // 2. 通过 WritableStream 写入数据
  await writable.write("第一行\n");
  await writable.write("第二行\n");
  await writable.close();
  console.log("文件已保存!");
}

13. Background Fetch

Background Fetch(后台获取)API 可以让网页在关闭或切后台时,仍然可以继续下载大文件(比如视频、离线包、游戏资源等)。

简单来说:Background Fetch 允许网页通过 Service Worker 在后台下载或上传大型文件,即使用户关闭了页面也不会中断任务

13.1. 为什么需要它

普通的 fetch() 请求在页面关闭后会被中断。

而如果是:

  • 要下载 超大视频/压缩包
  • 要上传大文件
  • 要在后台静默更新离线缓存

那么就需要 Bacnground Fetch。

13.2. 基本使用流程

// 1️. 注册 Service Worker
navigator.serviceWorker.register('/sw.js');
// 2️. 在主线程中发起后台下载
const bgFetch = await registration.backgroundFetch.fetch(
  'video-download', // 下载任务的唯一标识
  ['/videos/big-video.mp4'], // 要下载的资源
  {
    title: '下载大视频中...',
    icons: [{ sizes: '72x72', src: '/icon.png', type: 'image/png' }],
    downloadTotal: 100 * 1024 * 1024 // 可选:预估文件大小(100MB)
  }
);

然后在 Service Worker 里监听事件

// sw.js
self.addEventListener('backgroundfetchsuccess', event => {
  console.log('后台下载成功!');
  // 获取所有下载结果
  event.waitUntil(async function () {
    const records = await event.registration.matchAll();
    for (const record of records) {
      const response = await record.responseReady;
      const blob = await response.blob();
      // 保存文件或缓存
      console.log('保存文件:', blob.size, '字节');
    }
  }());
});
self.addEventListener('backgroundfetchfail', event => {
  console.log('下载失败:', event.registration.id);
});
self.addEventListener('backgroundfetchabort', event => {
  console.log('下载被用户取消');
});

背景获取的事件生命周期 :

  • backgroundfetchclick:用户点击通知
  • backgroundfetchsuccess:下载全部成功
  • backgroundfetchfail:部分或全部失败
  • backgroundfetchabrot:用户取消下载

13.3. 应用场景

  1. 视频下载:用户离开页面时视频仍能继续缓存
  2. 离线应用更新:后台下载新版本资源包
  3. 大文件上传:用户切出页面后上传不被中断
  4. 后台通知:推送通知提醒用户

14. File System Access

File System Acess API 允许网页直接读取、写入和保存用户本地文件(需要用户授权)。这个 API 让 Web 应用拥有类似桌面应用的文件操作能力。

14.1. 语法

const [fh] = await showOpenFilePicker();
editor.value = await (await fh.getFile()).text();

File System Access API 提供了几个核心接口:

  • showOpenFilePicker:打开文件选择器,获取文件句柄( FileSystemFileHandle )
  • showSaveFilePicker:打开文件保存对话框,获取保存文件的句柄
  • showDirectoryPicker:打开目录选择器,获取文件夹句柄( FileSystemDirectoryHandle )
  • FileSystemFileHandle:表示一个文件可以读取内容或写入内容
  • FileSystemWritableFileStream:写入流,用于将数据写入文件

14.2. 代码演示

14.2.1. 打开文件并读取内容

  

  <script>
  document.getElementById("open").addEventListener("click", async () => {
    try {
      // 打开文件选择器
      const [fileHandle] = await window.showOpenFilePicker();
      const file = await fileHandle.getFile();
      // 读取内容
      const content = await file.text();
      document.getElementById("output").textContent = content;
    } catch (err) {
      console.error("读取文件失败:", err);
    }
  });
</script>
14.2.2. 写入文件(保存内容)

  <script>
  document.getElementById("save").addEventListener("click", async () => {
    try {
      // 打开保存文件对话框
      const handle = await window.showSaveFilePicker({
        suggestedName: "example.txt",
        types: [{
          description: "Text Files",
          accept: { "text/plain": [".txt"] },
        }],
      });
      // 创建写入流
      const writable = await handle.createWritable();
      await writable.write("这是写入的内容!");
      await writable.close();
      alert("文件已保存!");
    } catch (err) {
      console.error("写入文件失败:", err);
    }
  });
</script>
14.2.3. 读取文件夹中的多个文件

  <script>
  document.getElementById("openDir").addEventListener("click", async () => {
    try {
      const dirHandle = await window.showDirectoryPicker();
      for await (const [name, handle] of dirHandle.entries()) {
        if (handle.kind === "file") {
          const file = await handle.getFile();
          console.log(`文件名: ${name}, 大小: ${file.size}`);
        }
      }
    } catch (err) {
      console.error("访问文件夹失败:", err);
    }
  });
</script>
14.2.4. 修改文件内容
const [fileHandle] = await showOpenFilePicker();
const writable = await fileHandle.createWritable();
await writable.write("更新文件的新内容");
await writable.close();
14.2.5. 在线文本编辑器



  
  文件系统访问 Demo - 在线编辑器
  


  

在线文本编辑器

<script> let fileHandle = null; // 保存当前打开的文件句柄 // 打开文件 document.getElementById("openFile").addEventListener("click", async () => { try { const [handle] = await window.showOpenFilePicker({ types: [ { description: "文本文件", accept: { "text/plain": [".txt", ".md", ".json"] }, }, ], }); fileHandle = handle; const file = await handle.getFile(); const content = await file.text(); document.getElementById("editor").value = content; alert(`已打开文件: ${file.name}`); } catch (err) { console.error("打开文件失败:", err); } }); // 保存文件 document.getElementById("saveFile").addEventListener("click", async () => { try { if (!fileHandle) { // 如果还没有文件句柄,就弹出保存窗口 fileHandle = await window.showSaveFilePicker({ suggestedName: "new-file.txt", types: [ { description: "文本文件", accept: { "text/plain": [".txt", ".md", ".json"] }, }, ], }); } const writable = await fileHandle.createWritable(); await writable.write(document.getElementById("editor").value); await writable.close(); alert("文件已保存!"); } catch (err) { console.error("保存文件失败:", err); } }); </script>

14.3. 总结

打开文件: showOpenFilePicker()

保存文件: showSaveFilePicker()

选择文件夹: showDirectoryPicker()

写文件:handle.createWritable()

读文件:handle.getFile()

15. Clipboard

Clipboard API 是浏览器原生提供的“复制/粘贴”能力。 可以用它 复制文本 / 图片到系统剪贴板,也可以 从剪贴板读取内容

15.1. 语法

await navigator.clipboard.writeText('要复制的文字') //复制
await navigator.clipboard.readText()// 粘贴

15.2. 功能

功能

方法

说明

写入文本

navigator.clipboard.writeText(text)

把字符串复制到剪贴板

读取文本

navigator.clipboard.readText()

从剪贴板读取文本内容

写入多种类型

navigator.clipboard.write(items)

可写入图片、HTML 等

读取多种类型

navigator.clipboard.read()

可读取图片、HTML 等(需要 HTTPS)

15.3. 代码演示



  
    
    Clipboard API Demo
    
  
  
    

Clipboard API Demo

<script> const input = document.getElementById("inputText"); const copyBtn = document.getElementById("copyBtn"); const pasteBtn = document.getElementById("pasteBtn"); // 复制到剪贴板 copyBtn.addEventListener("click", async () => { try { await navigator.clipboard.writeText(input.value); alert("已复制到剪贴板!"); } catch (err) { alert("复制失败:" + err); } }); // 从剪贴板读取 pasteBtn.addEventListener("click", async () => { try { const text = await navigator.clipboard.readText(); input.value = text; alert("已从剪贴板读取内容!"); } catch (err) { alert("粘贴失败:" + err); } }); </script>

16. URLSearchParams

URLSearchParams 用于解析、构建和操作 URL 查询参数 (就是 ?key=value&key2=value2 那部分)

简单理解:URLSearchParams 就是一个可以帮你轻松读取和修改 URL 查询字符串的工具类

16.1. 基本语法

const params = new URLSearchParams('?name=Tom&age=20')

16.2. 常用方法

方法

作用

示例

get(key)

获取参数值

params.get('name') // "Tom"

set(key, value)

修改或新增参数

params.set('age', 25)

append(key, value)

添加重复参数

params.append('hobby', 'music')

delete(key)

删除参数

params.delete('age')

has(key)

判断是否存在

params.has('name') // true

toString()

转成查询字符串

params.toString() // "name=Tom&hobby=music"

16.3. 代码演示

一句话总结:URLSearchParams 就是专门帮你读、改、拼接 URL 查询字符串的工具,比自己用正则拆字符串安全又简单。

16.3.1. 解析当前 URL 参数
// 当前网址: https://example.com/?user=mingfei&role=admin
const params = new URLSearchParams(window.location.search)
console.log(params.get('user')) // mingfei
console.log(params.get('role')) // admin
16.3.2. 动态构建 URL
const params = new URLSearchParams()
params.set('page', 2)
params.set('limit', 10)
const url = `https://api.example.com/users?${params.toString()}`
console.log(url)
// 输出: https://api.example.com/users?page=2&limit=10
16.3.3. 遍历所有参数
const params = new URLSearchParams('?a=1&b=2&c=3')
for (const [key, value] of params) {
  console.log(key, value)
}
// 输出:
// a 1
// b 2
// c 3
16.3.4. 支持重复参数(数组场景)
const params = new URLSearchParams()
params.append('tag', 'vue')
params.append('tag', 'javascript')
console.log(params.toString())
// tag=vue&tag=javascript
console.log(params.getAll('tag'))
// ['vue', 'javascript']
16.3.5. 合并参数
const base = new URLSearchParams('a=1&b=2')
const extra = new URLSearchParams('b=3&c=4')
for (const [key, value] of extra) base.set(key, value)
console.log(base.toString()) // a=1&b=3&c=4

17. structuredClone

strycturedClone 用来实现深拷贝(deep clone)。

简单来说:structuredClone() 是浏览器原生提供的、用来安全复制任意 JS 对象的深拷贝函数,它支持 MapSetDateArrayBuffer 等复杂类型。

17.1. 语法

const newObj = structuredClone(originalObj)
  • 参数:要克隆的对象
  • 返回值:一个全新的、与原对象完全独立的副本

17.2. 代码演示

const user = {
  name: "Mingfei",
  info: { age: 22, hobby: ["Vue", "React"] },
}
const copy = structuredClone(user)
copy.info.age = 30
console.log(user.info.age) // 22 不受影响

这就是深拷贝:嵌套对象也会被完整复制。

17.3. 支持的类型

类型

是否支持

Object / Array

Date

RegExp

❌否(会抛错)

Map / Set

TypedArray

ArrayBuffer

Blob / File

DOM Node

❌ 否(会报错)

Function

❌ 否(会报错)

17.4. 与 JSON 方式的区别

特性

JSON.parse(JSON.stringify(obj))

structuredClone(obj)

深拷贝

支持循环引用

支持 MapSet

支持 DateArrayBuffer

丢失函数

性能

一般

更快且更安全

structuredClone() 是现代 JS 官方提供的“万能深拷贝神器”,比 JSON 方案更安全、更快、更全面,支持复杂数据结构和循环引用。

18. Intl.NumberFormat

Intl.numberFormat 是一个非常使用的原生国际化(I18n) API。可以在不同语言、地区下自动格式化数字、货币、百分比等。

简单来说:Intl.NumberFormat 是 JavaScript 内置的国际化格式化工具,用来把数字根据语言与地区规则格式化成符合当地习惯的字符串。

18.1. 语法

const formatter = new Intl.NumberFormat([locales], [options])
formatter.format(number)
  • locales:地区/语言代码,如 "zh-CN""en-US""ja-JP"
  • options:控制格式(货币、百分比、小数位数等)

18.2. 代码演示

18.2.1. 最简单的例子
const num = 1234567.89
console.log(new Intl.NumberFormat().format(num))
// 在中文环境中输出:1,234,567.89

自动加上千位分隔符(,),不同语言下表现不同。

18.2.2. 格式化为货币
const price = 1234.5
console.log(new Intl.NumberFormat('zh-CN', {
  style: 'currency',
  currency: 'CNY'
}).format(price))
// 输出:¥1,234.50
console.log(new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(price))
// 输出:$1,234.50

根据地区自动加货币符号,currency 支持 USDCNYJPYEUR 等。

18.2.3. 格式化百分比
const ratio = 0.456
console.log(new Intl.NumberFormat('zh-CN', {
  style: 'percent',
  maximumFractionDigits: 1
}).format(ratio))
// 输出:45.6%
18.2.4. 指定最大小数位数
const num = 1234.567
console.log(new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2
}).format(num))
// 输出:1,234.57
18.2.5. 货币与数字混合控制
const salary = 9876543.21
const formatter = new Intl.NumberFormat('de-DE', { // 德国
  style: 'currency',
  currency: 'EUR',
})
console.log(formatter.format(salary))
// 输出:9.876.543,21 €

注意:欧洲习惯用 . 做千分位,, 做小数点。

18.2.6. 紧凑显示(K/M/B)
const views = 1234567
console.log(new Intl.NumberFormat('en', {
  notation: 'compact',
  compactDisplay: 'short'
}).format(views))
// 输出:1.2M

适合展示粉丝数、播放量、点赞数等场景。

18.3. 常用选项总结表

选项

作用

style

'decimal' / 'currency / 'percent' / 'unit'

currency

设置货币代码(如 'CNY''USD'

notation

'standard' / 'scientific' / 'compact'

compactDisplay

'short' / 'long'(配合 compact 使用)

minimumFractionDigits

最少保留小数位

maximumFractionDigits

最多保留小数位

unit

'kilometer' / 'liter' / 'hour' 等(style 必须是 'unit'

19. EyeDropper

EyeDropper 可以让用户在浏览器中取色(吸管工具),就像 Photoshop 或 Figma 那样。它是完全零依赖的浏览器原生能力。

19.1. 语法

const eyeDropper = new EyeDropper();
eyeDropper.open()
  .then(result => {
    console.log(result.sRGBHex); // 输出颜色字符串,如 #1a2b3c
  })
  .catch(err => {
    console.error('用户取消或取色失败:', err);
  });

19.2. 代码演示


<script> const btn = document.getElementById('pickColor'); const box = document.getElementById('colorBox'); btn.addEventListener('click', async () => { if (!window.EyeDropper) { alert('当前浏览器不支持 EyeDropper API'); return; } try { const eyeDropper = new EyeDropper(); const result = await eyeDropper.open(); // 打开取色工具 box.style.backgroundColor = result.sRGBHex; // 显示取到的颜色 console.log('取到的颜色:', result.sRGBHex); } catch (err) { console.log('用户取消取色或出错', err); } }); </script>

20. WebCodecs

WebCodes 它能让你直接访问浏览器底层的视频/音频编解码器(Codec),无需 ffmpeg、无需插件。

20.1. WebCOdecs 是什么

WebCodecs API 是一个底层多媒体编解码接口,允许 JavaScript 高效地:

  • 解码(decode) 视频或音频帧(比如 mp4、webm)
  • 编码(encode) 视频或音频帧(比如生成 webm 文件)
  • 在不经过 <video><audio> 的情况下处理媒体数据

它为浏览器多媒体处理提供了近乎原生的性能

20.2. 语法

const decoder = new VideoDecoder({
  output: frame => ctx.drawImage(frame, 0, 0),
  error: console.error
});
decoder.configure({ codec: 'vp09.00.10.08' });

20.3. 基本组成

WebCodecs 主要由以下几个核心类组成:

类名

功能

VideoDecoder

将压缩视频数据(如 H.264)解码为 VideoFrame

VideoEncoder

VideoFrame

编码为压缩格式

AudioDecoder

解码音频

AudioEncoder

编码音频

VideoFrame

视频帧对象,可直接绘制到 <canvas>

EncodedVideoChunk

压缩后的视频片段

20.4. 代码演示

20.4.1. 最基本的使用示例:解码视频帧
// 1️. 创建视频解码器
const decoder = new VideoDecoder({
  output: handleFrame, // 每当解码出一帧时触发
  error: (e) => console.error("解码错误:", e)
});
// 2️. 配置解码器
decoder.configure({
  codec: 'vp8', // 支持: vp8, vp9, avc1 (H.264), av01, 等
});
// 3️. 模拟输入压缩视频数据
const chunk = new EncodedVideoChunk({
  type: "key", // 关键帧或 delta 帧
  timestamp: 0,
  data: new Uint8Array([/* 视频数据字节流 */])
});
// 4️. 输入视频数据进行解码
decoder.decode(chunk);
// 5️. 每帧输出回调
function handleFrame(frame) {
  console.log("解码到视频帧:", frame);
  // 可以直接绘制到 canvas
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  ctx.drawImage(frame, 0, 0);
  frame.close(); // 必须手动释放内存
}
20.4.2. 编码示例:生成视频数据
const encoder = new VideoEncoder({
  output: (chunk) => console.log("编码完成片段:", chunk),
  error: (e) => console.error(e),
});
encoder.configure({
  codec: 'vp8',
  width: 640,
  height: 480,
  bitrate: 1_000_000,
  framerate: 30,
});
// 用 Canvas 创建一帧
const canvas = document.createElement("canvas");
canvas.width = 640;
canvas.height = 480;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "skyblue";
ctx.fillRect(0, 0, 640, 480);
// 将 Canvas 转成 VideoFrame
const frame = new VideoFrame(canvas, { timestamp: 0 });
// 编码该帧
encoder.encode(frame);
frame.close();

20.5. 性能优势

  • 直接使用系统硬件编解码器(GPU 加速);
  • 无需引入庞大的 ffmpeg.js;
  • 延迟极低,可用于实时视频流、虚拟会议、游戏录制等。

20.6. 应用场景

  1. 视频会议:低延迟推流、实时解码。
  2. 在线录屏:实时采集和编码
  3. 视频编辑器:本地转码、剪辑
  4. WebRTC 优化:自定义流处理前后端传输
  5. AI 视频分析:解码帧后传入 WebGPU/WebGL
posted @ 2026-02-03 21:34  gccbuaa  阅读(0)  评论(0)    收藏  举报