手记

浏览器藏了这么多神器,你居然不知道?

你以为浏览器只能打开网页?打开控制台敲 console.log?其实浏览器早就给你准备好了各种"神器"——只是 99% 的人从来没注意过。这些 API 之所以存在,是因为浏览器在发展过程中遇到了真实的问题。

今天,用工具箱的故事,来讲讲浏览器内置的 Web API。


原文地址

墨渊书肆/浏览器藏了这么多神器,你居然不知道?


浏览器是个工具箱

为什么浏览器要内置这么多 API?

浏览器就像一个万能工具箱

  • 木工要锤子、锯子、螺丝刀

  • 前端要检测元素、监控性能、访问硬件

浏览器面临的场景越来越复杂:

  • 检测页面何时可见/隐藏

  • 监听某个元素何时出现在屏幕

  • 监听 DOM 元素的大小变化

  • 监控网页性能数据

  • 访问电池、位置、剪贴板等硬件

所以浏览器厂商(Google、Mozilla、Apple)就把常见需求做成标准 API,让开发者直接用。

兼容性问题

在开始之前,先说个重要的事:不是所有 API 所有浏览器都支持

APIChromeFirefoxSafariEdge
IntersectionObserver✅ 51+✅ 55+✅ 12.1+✅ 79+
MutationObserver✅ 18+✅ 14+✅ 6+✅ 12+
ResizeObserver✅ 64+✅ 31+✅ 13.1+✅ 79+
PerformanceObserver✅ 56+✅ 58+✅ 11+✅ 79+
Page Visibility API✅ 33+✅ 18+✅ 6.1+✅ 12+
Battery API✅ 50+✅ 10+❌ 不支持✅ 79+
Clipboard API✅ 66+✅ 63+✅ 13.1+✅ 79+
Geolocation API✅ 5+✅ 3.5+✅ 3+✅ 12+

注意:Battery API 在 Safari 和大多数移动浏览器上不支持,使用时要做好兼容处理。


IntersectionObserver — 懒加载和无限滚动的救星

故事的起因:scroll 事件太慢了

十年前,前端要检测"某个元素是否出现在屏幕上",只能这样写:

javascript体验AI代码助手代码解读复制代码window.addEventListener('scroll', () => {   const rect = element.getBoundingClientRect();   if (rect.top < window.innerHeight) {     loadImage();   } });

这段代码有什么问题?

问题说明
性能差scroll 事件每秒触发几十次,每次都计算 rect
无法批量多个元素要写多个监听
滚动卡顿计算太频繁,导致页面卡

就像门口站了个保安:每进来一个人,他都要站起来看一眼是不是 VIP——累死了。

IntersectionObserver 的原理

IntersectionObserver = 交叉观察器。

浏览器提供了这个 API,让你能高效地检测元素是否进入视口

原理很简单:

yaml体验AI代码助手代码解读复制代码IntersectionObserver 工作原理: ┌────────────────────────────┐ │       视口(Viewport)      │ │  ┌────────────────────┐    │ │  │     target 元素     │    │ │  │                    │    │ │  └────────────────────┘    │ └────────────────────────────┘           ↓    元素进入视口?浏览器自动通知你

基本用法

javascript体验AI代码助手代码解读复制代码const observer = new IntersectionObserver((entries) => {   entries.forEach(entry => {     console.log('元素可见性:', entry.isIntersecting);     console.log('交叉比例:', entry.intersectionRatio);   }); }, {   root: null,              // null = 浏览器视口   rootMargin: '0px',       // 扩大/缩小检测区域   threshold: 0.5            // 50% 可见时触发 });  observer.observe(element);

配置参数详解

参数作用示例
root参考视口null=浏览器窗口,element=某个容器
rootMargin视口的扩展区域'100px'=提前100px就触发
threshold触发时机0=刚出现,0.5=一半可见,1=完全可见

实际应用:懒加载图片

这是最经典的应用场景: 0 1 2 3 4 5 6 7 8 9

html体验AI代码助手代码解读复制代码<img data-src="real-image.jpg" class="lazy" alt="加载中..."> <img data-src="real-image2.jpg" class="lazy" alt="加载中...">  <script>   const images = document.querySelectorAll('.lazy');    const observer = new IntersectionObserver((entries) => {     entries.forEach(entry => {       if (entry.isIntersecting) {         const img = entry.target;         img.src = img.dataset.src;      // 加载真实图片         img.classList.remove('lazy');    // 移除占位符样式         observer.unobserve(img);         // 加载完停止观察       }     });   }, {     rootMargin: '100px'  // 提前 100px 开始加载   });    images.forEach(img => observer.observe(img)); </script>

实际应用:无限滚动

电商网站、社交媒体最常用的"加载更多":

javascript体验AI代码助手代码解读复制代码const observer = new IntersectionObserver((entries) => {   entries.forEach(entry => {     if (entry.isIntersecting) {       loadMoreItems().then(() => {         // 新内容加载后,继续观察新的 loading 元素         observer.observe(document.querySelector('.loading'));       });     }   }); }, {   rootMargin: '200px'  // 距离底部 200px 就开始加载 });  observer.observe(document.querySelector('.loading'));


MutationObserver — DOM 变化探测器

故事的起因:jQuery 时代的噩梦

很久以前,前端要监听 DOM 变化是这样写的:

javascript体验AI代码助手代码解读复制代码$('#container').bind('DOMNodeInserted', function() {   console.log('有新内容了'); });

后来有了 MutationEvent

javascript体验AI代码助手代码解读复制代码document.addEventListener('DOMNodeInserted', (e) => {   console.log('有新节点:', e.target); });

但这些事件有严重问题:

问题说明
性能灾难每次 DOM 变化都触发,变化多了直接卡死
不准确不能区分是内容变了还是属性变了
已废弃现代浏览器不再推荐使用

MutationObserver 的原理

MutationObserver = 变化观察器。

浏览器用它来高效地监听 DOM 变化,变化会被批量收集,一次性通知你。

yaml体验AI代码助手代码解读复制代码MutationObserver 原理: ┌──────────────────────────────┐ │           DOM 树             │ │    <div id="app">            │ │      <p>你好</p>   ← 变化     │ │    </div>                    │ └──────────────────────────────┘           ↓ 变化被收集 → 批量通知 → 一次回调

基本用法

javascript体验AI代码助手代码解读复制代码const observer = new MutationObserver((mutations) => {   // mutations 数组包含所有变化   mutations.forEach(mutation => {     console.log('变化类型:', mutation.type);     console.log('变化节点:', mutation.target);   }); });  observer.observe(document.body, {   childList: true,       // 监听子节点增删   subtree: true,        // 监听所有后代节点   attributes: true,     // 监听属性变化   attributeOldValue: true,  // 记录变化前的属性值   characterData: true,   // 监听文本变化   characterDataOldValue: true  // 记录变化前的文本 });

变化类型详解

type说明包含内容
childList子节点增删新增或删除的节点
attributes属性变化变化的属性名和值
characterData文本变化变化前后的文本内容
yaml体验AI代码助手代码解读复制代码变化收集流程: 1. DOM 发生变化 2. 变化被记录到队列(不立即通知) 3. 变化积累一定数量或时间后 4. 批量通知观察者

实际应用:表单变化检测

监听表单是否有未保存的修改:

javascript体验AI代码助手代码解读复制代码const observer = new MutationObserver(() => {   form.hasChanges = true;  // 标记有变化 });  observer.observe(form, {   childList: true, subtree: true, attributes: true, characterData: true });  form.addEventListener('submit', () => {   form.hasChanges = false;   observer.disconnect(); });


ResizeObserver — 元素大小监听器

故事的起因:window.resize 太粗糙了

以前要监听元素大小变化,只能监听整个窗口:

javascript体验AI代码助手代码解读复制代码window.addEventListener('resize', () => {   console.log('窗口大小:', window.innerWidth, window.innerHeight); });

但这有两个问题:

问题说明
不精确只能监听窗口,不能监听某个 div
性能差窗口每次 resize 都触发,频率很高

ResizeObserver 的原理

ResizeObserver = 大小观察器。

专门用来监听任意元素的大小变化,精准高效。

yaml体验AI代码助手代码解读复制代码ResizeObserver 原理: ┌─────────────────────────┐ │  <div class="box">      │ │    内容随着大小变化        │ │  </div>                 │ └─────────────────────────┘           ↓   大小变化 → 浏览器自动通知           ↓  entry.contentRect 包含新尺寸

基本用法

javascript体验AI代码助手代码解读复制代码const observer = new ResizeObserver((entries) => {   entries.forEach(entry => {     const { width, height } = entry.contentRect;     console.log(`元素大小: ${width} x ${height}`);   }); });  observer.observe(boxElement);  // 停止观察 observer.unobserve(boxElement); observer.disconnect();

实际应用:响应式布局

根据容器大小切换布局:

javascript体验AI代码助手代码解读复制代码const observer = new ResizeObserver((entries) => {   entries.forEach(entry => {     const width = entry.contentRect.width;     const target = entry.target;      // 移除旧布局类     target.classList.remove('mobile', 'tablet', 'desktop');      // 根据宽度添加新布局类     if (width < 600) {       target.classList.add('mobile');       renderMobileLayout(target);     } else if (width < 1024) {       target.classList.add('tablet');       renderTabletLayout(target);     } else {       target.classList.add('desktop');       renderDesktopLayout(target);     }   }); });  observer.observe(document.querySelector('.layout-container'));


PerformanceObserver — 性能监控器

故事的起因:Performance API 太难用

浏览器原生提供 performance.timing 等 API 来获取性能数据:

javascript体验AI代码助手代码解读复制代码// 获取页面加载时间 const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart; console.log('页面加载时间:', loadTime);

但问题是:

问题说明
一次性数据只在特定时间点有效
不实时无法监控动态加载的资源
不直观要自己计算各种时间差

PerformanceObserver 的原理

PerformanceObserver = 性能观察器。

让你实时监控浏览器的各种性能数据,浏览器会主动通知你。

yaml体验AI代码助手代码解读复制代码PerformanceObserver 能监控的数据: ┌───────────────────────────────────┐ │  longtask      - 长任务(>50ms)    │ │  paint         - 绘制时间(FP/FCP) │ │  resource      - 资源加载时间       │ │  navigation    - 页面导航时间       │ │  mark          - 自定义标记         │ │  measure       - 自定义测量         │ └───────────────────────────────────┘

基本用法

javascript体验AI代码助手代码解读复制代码const observer = new PerformanceObserver((list) => {   list.getEntries().forEach(entry => {     console.log('性能数据:', entry);   }); });  // 观察长任务 observer.observe({ type: 'longtask', buffered: true });  // 观察绘制时间 observer.observe({ type: 'paint', buffered: true });  // 观察资源加载 observer.observe({ type: 'resource', buffered: true });

实际应用:检测长任务

javascript体验AI代码助手代码解读复制代码const observer = new PerformanceObserver((list) => {   list.getEntries().forEach(entry => {     console.warn('检测到长任务:', entry.duration, 'ms');      // 长任务超过 50ms,视为需要优化     if (entry.duration > 50) {       console.error('长任务位置:', entry.name);       console.error('开始时间:', entry.startTime);     }   }); });  observer.observe({ type: 'longtask', buffered: true });


Page Visibility API — 页面可见性检测

故事的起因:用户切换标签页你不知道

你有没有想过:

  • 用户打开了你的页面,然后切到别的标签页

  • 你的动画还在跑吗?定时器还在跑吗?

  • 如果是视频网站,用户切走了,视频还在播放吗?

以前的解决方案:

javascript体验AI代码助手代码解读复制代码window.addEventListener('blur', () => {   // 窗口失去焦点 });  window.addEventListener('focus', () => {   // 窗口获得焦点 });

但这不够精确——用户可能只是最小化了窗口,或者切换到了别的标签页

Page Visibility API 的原理

Page Visibility API 让你精确知道页面的可见状态

yaml体验AI代码助手代码解读复制代码visibilityState 的两种状态: ┌─────────────────────────────────────┐ │  visible      - 页面完全可见         │ │  hidden       - 页面被隐藏          │ └─────────────────────────────────────┘  触发场景: - 切换标签页 → hidden - 最小化窗口 → hidden - 关闭浏览器 → hidden - 切换应用 → hidden

基本用法

javascript体验AI代码助手代码解读复制代码document.addEventListener('visibilitychange', () => {   if (document.hidden) {     console.log('页面被隐藏了');     pauseAnimations();     stopPolling();   } else {     console.log('页面可见了');     resumeAnimations();     startPolling();   } });  console.log('当前状态:', document.visibilityState);

实际应用:标签页切换统计

javascript体验AI代码助手代码解读复制代码const metrics = {   visibleTime: 0,   hiddenTime: 0,   lastChangeTime: Date.now() };  document.addEventListener('visibilitychange', () => {   const now = Date.now();    if (document.hidden) {     // 开始隐藏,记录可见时长     metrics.visibleTime += now - metrics.lastChangeTime;     metrics.lastChangeTime = now;      // 暂停音乐/视频     video.pause();   } else {     // 结束隐藏,记录隐藏时长     metrics.hiddenTime += now - metrics.lastChangeTime;     metrics.lastChangeTime = now;      // 恢复音乐/视频     video.play();   } });  window.addEventListener('beforeunload', () => {   navigator.sendBeacon('/analytics', JSON.stringify(metrics)); });


Battery API — 电池状态检测

故事的起因:低电量时该省电

做 Web 应用时,你可能想过:

  • 电量低的时候,要不要关闭动画省电?

  • 正在充电时,能不能开启高性能模式?

  • 用户还能用多久?

这些问题,Battery API 都能回答。

Battery API 的兼容性

浏览器支持情况
Chrome✅ 50+
Firefox✅ 10+
Safari❌ 不支持
Edge✅ 79+

注意:Safari 和 iOS Safari 不支持 Battery API。如果你的用户主要是苹果设备,这个 API 就用不了。

Battery API 的原理

Battery API = 电池状态接口。

浏览器通过操作系统获取电池信息,暴露给 JavaScript。

yaml体验AI代码助手代码解读复制代码Battery API 数据来源: ┌─────────────────────────────────────┐ │         浏览器                        │ │  navigator.getBattery()              │ └─────────────────────────────────────┘           ↓ ┌─────────────────────────────────────┐ │         操作系统                      │ │  Windows / macOS / Linux            │ │  提供电池状态、电量、充电时间          │ └─────────────────────────────────────┘           ↓ ┌─────────────────────────────────────┐ │         硬件                          │ │  电池芯片提供实时数据                 │ └─────────────────────────────────────┘

基本用法

javascript体验AI代码助手代码解读复制代码navigator.getBattery().then(battery => {   console.log('是否在充电:', battery.charging);   console.log('电量:', (battery.level * 100).toFixed(0), '%');   console.log('剩余时间:', battery.dischargingTime / 60, '分钟');    // 监听电量变化   battery.addEventListener('levelchange', () => {     console.log('电量变化:', (battery.level * 100).toFixed(0), '%');   });    // 监听充电状态变化   battery.addEventListener('chargingchange', () => {     console.log('充电状态变化:', battery.charging ? '充电中' : '未充电');   }); });

实际应用:省电模式

javascript体验AI代码助手代码解读复制代码// 获取电池信息并初始化省电模式 navigator.getBattery().then(battery => {   // 根据电池状态更新页面   function updateMode() {     // 判断是否需要省电:没在充电 且 电量低于 20%     const shouldSavePower = !battery.charging && battery.level < 0.2;      // 切换 body 的省电样式类     document.body.classList.toggle('power-saving', shouldSavePower);      if (shouldSavePower) {       // 电量低:关闭动画、停止轮询、降低画质       disableAnimations();       stopAutoRefresh();       reduceRenderQuality();     } else {       // 电量充足:恢复正常模式       enableAnimations();       startAutoRefresh();       restoreRenderQuality();     }   }    // 监听电量变化和充电状态变化   battery.addEventListener('levelchange', updateMode);   battery.addEventListener('chargingchange', updateMode);    // 页面加载时先检查一次   updateMode(); });


Clipboard API — 剪贴板读写

故事的起因:以前只能靠 document.execCommand

以前复制文本到剪贴板,是这样的:

javascript体验AI代码助手代码解读复制代码// ❌ 这种方式已经废弃了 textarea.select(); document.execCommand('copy');

而且这种方式问题很多:

  • 只能复制文本

  • 体验差(会有选中效果)

  • API 废弃了

Clipboard API 的原理

Clipboard API 让你安全地读写剪贴板,支持文本、图片、任意数据。

方法作用
navigator.clipboard.readText()读取剪贴板文本
navigator.clipboard.writeText()写入文本到剪贴板
navigator.clipboard.read()读取任意数据(图片等)
navigator.clipboard.write()写入任意数据

注意:读写剪贴板需要用户授权!

基本用法

javascript体验AI代码助手代码解读复制代码// 复制文本 async function copyText(text) {   try {     await navigator.clipboard.writeText(text);     showToast('复制成功!');   } catch (err) {     console.error('复制失败:', err);   } }  // 读取剪贴板 async function readClipboard() {   try {     const text = await navigator.clipboard.readText();     console.log('剪贴板内容:', text);     return text;   } catch (err) {     console.error('读取失败:', err);   } }

实际应用:一键复制代码

javascript体验AI代码助手代码解读复制代码document.querySelectorAll('.code-block').forEach(block => {   const button = document.createElement('button');   button.className = 'copy-btn';   button.textContent = '复制';    button.addEventListener('click', async () => {     const code = block.textContent;      try {       await navigator.clipboard.writeText(code);       button.textContent = '已复制!';       setTimeout(() => {         button.textContent = '复制';       }, 2000);     } catch (err) {       button.textContent = '复制失败';     }   });    block.appendChild(button); });


Geolocation API — 地理位置

故事的起因:LBS 应用越来越火

地图、打车、外卖、社交软件……越来越多的应用需要知道用户的位置

Geolocation API 就是浏览器提供的定位接口

Geolocation API 的原理

Geolocation API = 地理位置接口。

浏览器会调用系统定位服务来获取位置。

yaml体验AI代码助手代码解读复制代码Geolocation 定位方式: ┌───────────────────────────────────┐ │  GPS 定位      - 精度最高(1-10米)  │ │  WLAN 定位     - 通过 WiFi 路由器    │ │  基站定位      - 通过手机信号塔       │ │  IP 定位       - 精度最低(城市级)   │ └───────────────────────────────────┘ 浏览器会自动选择最优方式

为什么需要用户授权?

原因说明
隐私位置信息属于敏感个人信息
法律要求GDPR 等法规要求明确授权
安全防止网站偷偷获取位置

基本用法

javascript体验AI代码助手代码解读复制代码navigator.geolocation.getCurrentPosition(   (position) => {     console.log('纬度:', position.coords.latitude);     console.log('经度:', position.coords.longitude);     console.log('精度:', position.coords.accuracy, '米');   },   (error) => {     console.error('获取失败:', error.message);   },   {     enableHighAccuracy: true,  // 高精度模式(更慢)     timeout: 5000,            // 超时时间     maximumAge: 0            // 不使用缓存   } );

监听位置变化

javascript体验AI代码助手代码解读复制代码const watchId = navigator.geolocation.watchPosition(   (position) => {     updateMapPosition(position.coords.latitude, position.coords.longitude);   },   (error) => {     console.error('监听失败:', error.message);   },   {     enableHighAccuracy: true,     timeout: 10000,     maximumAge: 30000  // 缓存 30 秒   } );  // 停止监听 navigator.geolocation.clearWatch(watchId);

实际应用:距离计算

javascript体验AI代码助手代码解读复制代码function calculateDistance(lat1, lon1, lat2, lon2) {   const R = 6371;  // 地球半径(公里)    const toRad = (deg) => deg * Math.PI / 180;    const dLat = toRad(lat2 - lat1);   const dLon = toRad(lon2 - lon1);    const a = Math.sin(dLat/2) * Math.sin(dLat/2) +             Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *             Math.sin(dLon/2) * Math.sin(dLon/2);    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));    return R * c; }  // 北京 (39.9042, 116.4074) 到 上海 (31.2304, 121.4737) const distance = calculateDistance(39.9042, 116.4074, 31.2304, 121.4737); console.log('北京到上海约:', distance.toFixed(0), '公里');


总结

神器一览表

API作用解决的问题兼容性
IntersectionObserver懒加载、无限滚动scroll 事件性能差✅ 主流都支持
MutationObserverDOM 变化监控MutationEvent 已废弃✅ 主流都支持
ResizeObserver元素大小监听window.resize 太粗糙✅ 主流都支持
PerformanceObserver性能监控性能数据不直观✅ 主流都支持
Page Visibility API页面可见性不知道用户是否在看✅ 主流都支持
Battery API电池状态无法感知电量⚠️ Safari 不支持
Clipboard API剪贴板读写execCommand 废弃✅ 主流都支持
Geolocation API地理位置需要 LBS 功能✅ 主流都支持

使用建议

yaml体验AI代码助手代码解读复制代码✅ 推荐大胆使用的: - IntersectionObserver:懒加载必备 - Page Visibility API:标签页切换必备 - Clipboard API:复制粘贴体验升级 - ResizeObserver:响应式布局神器  ⚠️ 需要兼容性处理的: - Battery API:先检测,不支持就降级 - Geolocation API:用户授权,注意隐私  ❌ 已废弃不要用的: - MutationEvent - document.execCommand(复制除外)


写在最后

现在你知道了:

  • 这些 API 不是浏览器随便加的,而是解决了真实问题

  • IntersectionObserver 让懒加载变得简单高效

  • MutationObserver 是 DOM 变化监控的唯一选择

  • ResizeObserver 解决了 window.resize 的痛点

  • PerformanceObserver 让性能监控变得直观

  • Page Visibility API 让你知道用户在看什么

  • Battery API 可以做省电优化(Safari 除外)

  • Clipboard API 是现代复制粘贴的标准方案

  • Geolocation API 让网页也能做 LBS 应用

每个 API 的存在都有其意义——用对了,问题迎刃而解

下次遇到相关场景,记得试试这些神器!



0人推荐
随时随地看视频
慕课网APP