JavaScript Web API

深入解析现代 JavaScript Web API:fetch 请求、IntersectionObserver 懒加载、MutationObserver DOM 监听、requestAnimationFrame 动画调度,以及 structuredClone 深拷贝。

为什么 Web API 是浏览器环境的核心

JavaScript 的强大之处不仅在于语言本身,更在于浏览器提供的丰富 Web API。从网络请求到文件处理,从页面渲染到性能监控,这些 API 让 JavaScript 能够操作浏览器、实现复杂的交互效果。

这篇文章解析现代前端开发中最常用的 Web API。


一、fetch API

1.1 基本用法

// GET 请求
const response = await fetch('/api/users');
const users = await response.json();

// POST 请求
const response = await fetch('/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ name: 'Alice', age: 30 })
});

const newUser = await response.json();

1.2 Request 和 Response 对象

// 创建 Request 对象
const request = new Request('/api/users', {
  method: 'GET',
  headers: new Headers({ 'Accept': 'application/json' })
});

// Response 对象属性
const response = await fetch(request);
console.log(response.ok);        // boolean
console.log(response.status);    // 200
console.log(response.headers);   // Headers 对象

// 克隆 Response(Response 只能读取一次)
const cloned = response.clone();

1.3 流式处理

// 流式读取大文件
const response = await fetch('/large-file.txt');
const reader = response.body.getReader();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(`Received ${value.length} bytes`);
}

二、URLSearchParams

2.1 基本操作

const params = new URLSearchParams();

params.set('name', 'Alice');
params.append('tag', 'developer');
params.append('tag', 'engineer');

console.log(params.toString()); // 'name=Alice&tag=developer&tag=engineer'

// 获取
console.log(params.get('name')); // 'Alice'
console.log(params.getAll('tag')); // ['developer', 'engineer']

// 检查
console.log(params.has('name')); // true

// 删除
params.delete('tag');

2.2 实际应用

// 构建查询参数
const params = new URLSearchParams({
  page: 1,
  limit: 10,
  sort: 'name'
});

const url = `/api/users?${params}`;

// 解析 URL 参数
const url = new URL(window.location.href);
const page = url.searchParams.get('page');

三、IntersectionObserver

3.1 懒加载图片

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '50px',
  threshold: 0.1
});

document.querySelectorAll('img[data-src]').forEach(img => {
  observer.observe(img);
});

3.2 曝光埋点

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const element = entry.target;
      const eventName = element.dataset.event;
      analytics.track(eventName, {
        visibility: entry.intersectionRatio
      });
    }
  });
}, {
  threshold: [0, 0.5, 1]
});

document.querySelectorAll('[data-event]').forEach(el => {
  observer.observe(el);
});

四、MutationObserver

4.1 监听 DOM 变化

const observer = new MutationObserver((mutations) => {
  mutations.forEach(mutation => {
    console.log('Mutation type:', mutation.type);
    console.log('Changed nodes:', mutation.addedNodes);
  });
});

observer.observe(document.body, {
  childList: true,    // 监听子节点添加/删除
  subtree: true,       // 监听所有后代
  attributes: true,    // 监听属性变化
  attributeOldValue: true
});

// 断开观察
observer.disconnect();

4.2 实际应用

// 检测某个元素是否被添加到 DOM
const observer = new MutationObserver((mutations, obs) => {
  const target = document.querySelector('#target');
  if (target) {
    console.log('Target element added to DOM');
    obs.disconnect(); // 找到后停止观察
  }
});

observer.observe(document.body, { childList: true, subtree: true });

五、requestAnimationFrame

5.1 动画基础

let start = null;
const duration = 1000;

function animate(timestamp) {
  if (!start) start = timestamp;
  const progress = timestamp - start;

  const element = document.querySelector('.moving');
  element.style.transform = `translateX(${progress}px)`;

  if (progress < duration) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

5.2 与 setInterval 的对比

// setInterval:不管浏览器状态,可能丢帧
setInterval(() => {
  updatePosition();
}, 16); // 约 60fps,但不稳定

// requestAnimationFrame:与浏览器刷新同步,更流畅
function gameLoop(timestamp) {
  updatePosition();
  requestAnimationFrame(gameLoop);
}
requestAnimationFrame(gameLoop);

六、structuredClone

6.1 深拷贝对象

const original = {
  name: 'Alice',
  nested: { value: 1 },
  date: new Date()
};

const clone = structuredClone(original);
console.log(clone.name); // 'Alice'
console.log(clone.nested.value); // 1
console.log(clone.date instanceof Date); // true

6.2 支持的类型

// 支持:Date、Map、Set、ArrayBuffer、TypedArray、Error
const obj = {
  map: new Map([['a', 1]]),
  set: new Set([1, 2, 3]),
  date: new Date(),
  error: new Error('test')
};

const clone = structuredClone(obj);
console.log(clone.map.get('a')); // 1
console.log(clone.set.has(2)); // true

七、延展阅读