前端请求库(取代JavaScript库的10个现代Web API及详细实施代码)

前端请求库(取代JavaScript库的10个现代Web API及详细实施代码)
取代JavaScript库的10个现代Web API及详细实施代码

为什么浏览器内置的API你还在用某个臃肿的Javascript库呢?用内置的API有什么好处呢?

Web平台经历了巨大演进,引入了强大的原生API,不再需要臃肿的JavaScript库。现代浏览器现已支持以往需要第三方依赖的复杂功能,从而带来更快的加载速度、更好的性能表现和更小的代码体积。

在分析了数百个生产环境应用后,我总结了10个最具影响力的原生Web API,它们能替代流行JavaScript库,同时提供更优的性能和用户体验。

1.Web Components API:替代React/Vue实现简单组件


无需框架依赖即可创建可复用组件

前端请求库(取代JavaScript库的10个现代Web API及详细实施代码)

class CustomButton extends HTMLElement {  constructor() {    super();    this.attachShadow({ mode: 'open' });  }    connectedCallback() {    this.render();    this.addEventListener('click', this.handleClick);  }    disconnectedCallback() {    this.removeEventListener('click', this.handleClick);  }    static get observedAttributes() {    return ['variant', 'disabled'];  }    attributeChangedCallback(name, oldValue, newValue) {    if (oldValue !== newValue) {      this.render();    }  }    render() {    const variant = this.getAttribute('variant') || 'primary';    const disabled = this.hasAttribute('disabled');        this.shadowRoot[xss_clean] = `      <style>        button {          padding: 12px 24px;          border: none;          border-radius: 6px;          cursor: pointer;          font-family: inherit;          font-size: 14px;          transition: all 0.2s ease;        }                .primary {          background: #007bff;          color: white;        }                .secondary {          background: #6c757d;          color: white;        }                button:hover:not(:disabled) {          transform: translateY(-1px);          box-shadow: 0 4px 8px rgba(0,0,0,0.2);        }                button:disabled {          opacity: 0.6;          cursor: not-allowed;        }      </style>      <button class="${variant}" ${disabled ? 'disabled' : ''}>        <slot></slot>      </button>    `;  }    handleClick = (event) => {    if (!this.hasAttribute('disabled')) {      this.dispatchEvent(new CustomEvent('custom-click', {        detail: { originalEvent: event },        bubbles: true      }));    }  }}customElements.define('custom-button', CustomButton);// 用法// <custom-button variant="primary">点击我</custom-button>

性能优势:相比React组件,简单UI元素代码体积减少80%

2.Intersection Observer API:替代滚动事件库


高效检测元素可见性,无需滚动事件监听器

class LazyImageLoader {  constructor(options = {}) {    this.options = {      rootMargin: '50px 0px',      threshold: 0.1,      ...options    };        this.observer = new IntersectionObserver(      this.handleIntersection.bind(this),      this.options    );        this.loadingImages = new Set();  }    observe(img) {    if (img.dataset.src && !this.loadingImages.has(img)) {      this.observer.observe(img);    }  }    handleIntersection(entries) {    entries.forEach(entry => {      if (entry.isIntersecting) {        this.loadImage(entry.target);        this.observer.unobserve(entry.target);      }    });  }    async loadImage(img) {    this.loadingImages.add(img);        try {      // 预加载图片      const imageLoader = new Image();      imageLoader.src = img.dataset.src;            await new Promise((resolve, reject) => {        imageLoader.onload = resolve;        imageLoader.onerror = reject;      });            // 为加载图片实施丝滑过度      img.style.opacity = '0';      img.src = img.dataset.src;            img.onload = () => {        img.style.transition = 'opacity 0.3s ease';        img.style.opacity = '1';        img.classList.add('loaded');      };          } catch (error) {      console.error('Failed to load image:', error);      img.classList.add('error');    } finally {      this.loadingImages.delete(img);    }  }    disconnect() {    this.observer.disconnect();    this.loadingImages.clear();  }}// 无限滚动实施class InfiniteScroll {  constructor(container, loadMore, options = {}) {    this.container = container;    this.loadMore = loadMore;    this.loading = false;        this.sentinel = document.createElement('div');    this.sentinel.style.height = '1px';    container.appendChild(this.sentinel);        this.observer = new IntersectionObserver(      this.handleIntersection.bind(this),      { rootMargin: '100px', ...options }    );        this.observer.observe(this.sentinel);  }    async handleIntersection(entries) {    const entry = entries[0];        if (entry.isIntersecting && !this.loading) {      this.loading = true;            try {        const hasMore = await this.loadMore();        if (!hasMore) {          this.observer.disconnect();        }      } catch (error) {        console.error('Failed to load more content:', error);      } finally {        this.loading = false;      }    }  }}

3.Resize Observer API:替代窗口缩放库


高效监控元素尺寸变化

class ResponsiveComponent {  constructor(element) {    this.element = element;  // 需要响应式处理的DOM元素    // 定义响应式断点(单位:像素)    this.breakpoints = {      small: 320,    // 小屏幕(手机竖屏)      medium: 768,   // 中等屏幕(平板)      large: 1024,   // 大屏幕(笔记本)      xlarge: 1440   // 超大屏幕(桌面显示器)    };        // 创建ResizeObserver实例监听元素尺寸变化    this.resizeObserver = new ResizeObserver(      this.handleResize.bind(this)  // 绑定this上下文    );        // 开始观察目标元素    this.resizeObserver.observe(element);  }    // 处理元素尺寸变化的回调函数  handleResize(entries) {    entries.forEach(entry => {      // 从内容矩形中解构获取当前宽度和高度      const { width, height } = entry.contentRect;      // 根据当前宽度获取对应的断点类型      const currentBreakpoint = this.getBreakpoint(width);            // 更新CSS自定义属性(可在CSS中通过var()引用)      this.element.style.setProperty('--element-width', `${width}px`);      this.element.style.setProperty('--element-height', `${height}px`);            // 更新data-size属性(用于CSS选择器定位)      this.element.dataset.size = currentBreakpoint;            // 触发自定义事件(允许外部监听响应式变化)      this.element.dispatchEvent(new CustomEvent('element-resize', {        detail: {           width,               // 当前元素宽度          height,              // 当前元素高度          breakpoint: currentBreakpoint  // 当前断点类型        }      }));            // 调用断点变化处理函数      this.handleBreakpointChange(currentBreakpoint, width, height);    });  }    // 根据宽度确定当前断点类型  getBreakpoint(width) {    if (width < this.breakpoints.small) return 'xsmall';  // 超小屏幕    if (width < this.breakpoints.medium) return 'small';  // 小屏幕    if (width < this.breakpoints.large) return 'medium';  // 中等屏幕    if (width < this.breakpoints.xlarge) return 'large';  // 大屏幕    return 'xlarge';  // 超大屏幕  }    // 处理不同断点的响应式行为  handleBreakpointChange(breakpoint, width, height) {    // 首先移除所有可能存在的布局类    this.element.classList.remove(      'mobile-layout',      'tablet-layout',      'desktop-layout'    );        // 根据断点类型添加对应的布局类    switch (breakpoint) {      case 'small':        this.element.classList.add('mobile-layout');  // 移动端布局        break;      case 'medium':        this.element.classList.add('tablet-layout');  // 平板布局        break;      default:        this.element.classList.add('desktop-layout'); // 桌面端布局    }  }    // 停止监听并清理资源  disconnect() {    this.resizeObserver.disconnect();  // 停止所有观察  }}

4.支持流式传输的Fetch API:替代Axios等HTTP库


用原生能力处理HTTP请求

class AdvancedHTTP {  constructor(baseURL = '', options = {}) {    this.baseURL = baseURL;    this.defaultOptions = {      headers: {        'Content-Type': 'application/json'      },      ...options    };        // 初始化拦截器对象,包含请求拦截器和响应拦截器数组    this.interceptors = {      request: [],      response: []    };  }    addRequestInterceptor(interceptor) {    // 添加请求拦截器到拦截器数组    this.interceptors.request.push(interceptor);  }    addResponseInterceptor(interceptor) {    // 添加响应拦截器到拦截器数组    this.interceptors.response.push(interceptor);  }    async request(url, options = {}) {    // 合并默认配置和传入的配置选项    let config = {      ...this.defaultOptions,      ...options,      headers: {        ...this.defaultOptions.headers,        ...options.headers      }    };        // 应用所有请求拦截器,按顺序执行    for (const interceptor of this.interceptors.request) {      config = await interceptor(config);    }        try {      // 拼接完整的请求URL      const fullURL = this.baseURL + url;      // 发送fetch请求      let response = await fetch(fullURL, config);            // 应用所有响应拦截器,按顺序执行      for (const interceptor of this.interceptors.response) {        response = await interceptor(response);      }            return response;    } catch (error) {      // 请求失败时抛出错误      throw new Error(`HTTP请求失败: ${error.message}`);    }  }    // 流式响应处理  async streamingRequest(url, onData, options = {}) {    // 发送请求获取响应    const response = await this.request(url, {      ...options,      headers: {        ...options.headers,        'Accept': 'text/plain'      }    });        // 检查响应是否支持流式读取    if (!response.body) {      throw new Error('不支持流式传输');    }        // 获取响应体的读取器    const reader = response.body.getReader();    // 创建文本解码器    const decoder = new TextDecoder();        try {      // 循环读取流数据      while (true) {        // 读取一块数据        const { done, value } = await reader.read();        if (done) break; // 如果读取完成则退出循环                // 解码数据块        const chunk = decoder.decode(value, { stream: true });        // 调用回调函数处理数据块        await onData(chunk);      }    } finally {      // 释放读取器锁      reader.releaseLock();    }  }    // 带进度跟踪的文件上传  async uploadWithProgress(url, file, onProgress) {    return new Promise((resolve, reject) => {      // 创建XMLHttpRequest对象      const xhr = new XMLHttpRequest();            // 监听上传进度事件      xhr.upload.addEventListener('progress', (event) => {        if (event.lengthComputable) {          // 计算上传进度百分比          const percentComplete = (event.loaded / event.total) * 100;          // 调用进度回调函数          onProgress(percentComplete);        }      });            // 监听加载完成事件      xhr.addEventListener('load', () => {        if (xhr.status >= 200 && xhr.status < 300) {          // 请求成功,解析响应数据          resolve(JSON.parse(xhr.responseText));        } else {          // 请求失败,抛出错误          reject(new Error(`上传失败: ${xhr.statusText}`));        }      });            // 监听错误事件      xhr.addEventListener('error', () => {        // 发生错误,抛出错误        reject(new Error('上传失败'));      });            // 创建FormData对象并添加文件      const formData = new FormData();      formData.append('file', file);            // 打开连接并发送请求      xhr.open('POST', this.baseURL + url);      xhr.send(formData);    });  }    // 便捷的GET请求方法  get(url, options) {    return this.request(url, { ...options, method: 'GET' });  }    // 便捷的POST请求方法  post(url, data, options) {    return this.request(url, {      ...options,      method: 'POST',      body: JSON.stringify(data)    });  }    // 便捷的PUT请求方法  put(url, data, options) {    return this.request(url, {      ...options,      method: 'PUT',      body: JSON.stringify(data)    });  }    // 便捷的DELETE请求方法  delete(url, options) {    return this.request(url, { ...options, method: 'DELETE' });  }}

5.Web Animations API:替代动画库


无需外部依赖创建流畅动画

class AnimationManager {  constructor() {    // 使用Map存储单个动画实例,键为自动生成的ID,值为Animation对象    this.animations = new Map();    // 使用Map存储动画序列,键为自动生成的ID,值为Animation对象数组    this.sequences = new Map();  }    // 基础动画方法,支持缓动效果  animate(element, keyframes, options = {}) {    // 创建动画实例,设置默认参数:300ms时长、ease-out缓动、forwards填充模式    const animation = element.animate(keyframes, {      duration: 300,      easing: 'ease-out',      fill: 'forwards',      ...options // 合并传入的自定义选项    });        // 生成唯一ID:元素ID(若无则用'element') + 时间戳    const id = `${element.id || 'element'}_${Date.now()}`;    // 将动画实例存入Map    this.animations.set(id, animation);        // 动画结束时自动清理Map中的记录    animation.addEventListener('finish', () => {      this.animations.delete(id);    });        return animation; // 返回动画实例以便链式调用  }    // 淡入动画效果  fadeIn(element, duration = 300) {    // 定义关键帧:从透明且下移20px到完全不透明且回到原位    return this.animate(element, [      { opacity: 0, transform: 'translateY(20px)' },      { opacity: 1, transform: 'translateY(0)' }    ], { duration }); // 应用传入的时长参数  }    // 滑动动画效果  slideIn(element, direction = 'left', duration = 300) {    // 定义不同方向的滑动起始和结束位置    const transforms = {      left: ['translateX(-100%)', 'translateX(0)'],    // 从左侧滑入      right: ['translateX(100%)', 'translateX(0)'],   // 从右侧滑入      up: ['translateY(-100%)', 'translateY(0)'],     // 从上方滑入      down: ['translateY(100%)', 'translateY(0)']     // 从下方滑入    };        // 使用对应方向的transform关键帧创建动画    return this.animate(element, [      { transform: transforms[direction][0] },      { transform: transforms[direction][1] }    ], { duration });  }    // 弹性动画效果  spring(element, keyframes, options = {}) {    // 定义弹性缓动曲线    const springEasing = 'cubic-bezier(0.68, -0.55, 0.265, 1.55)';        // 创建带有弹性效果的动画    return this.animate(element, keyframes, {      duration: 600,       // 默认600ms时长      easing: springEasing,// 应用弹性缓动      ...options           // 合并其他自定义选项    });  }    // 动画序列执行  async sequence(animations) {    // 生成唯一序列ID    const id = `sequence_${Date.now()}`;    const sequence = []; // 存储序列中的动画实例        // 遍历动画配置数组    for (const animationConfig of animations) {      const { element, keyframes, options, delay = 0 } = animationConfig;            // 如果有延迟设置,等待指定时间      if (delay > 0) {        await new Promise(resolve => setTimeout(resolve, delay));      }            // 创建并执行动画      const animation = this.animate(element, keyframes, options);      sequence.push(animation); // 将动画加入序列            // 如果配置要求等待动画完成(默认行为),则等待animation.finished Promise      if (options?.wait !== false) {        await animation.finished;      }    }        // 将完成的序列存入Map    this.sequences.set(id, sequence);    return sequence; // 返回序列数组以便外部控制  }    // 交错动画效果  stagger(elements, keyframes, options = {}) {    // 从选项中提取交错延迟时间(默认100ms),其余选项保留    const { staggerDelay = 100, ...animOptions } = options;        // 为每个元素创建带动画的Promise,按索引计算延迟时间    return elements.map((element, index) => {      return this.animate(element, keyframes, {        ...animOptions,          // 合并基础动画选项        delay: index * staggerDelay // 每个元素递增延迟      });    });  }    // 视差滚动效果  parallax(elements, scrollContainer = window) {    // 定义更新视差效果的函数    const updateParallax = () => {      // 获取当前滚动位置(兼容window和自定义容器)      const scrollTop = scrollContainer === window         ? window.pageYOffset         : scrollContainer.scrollTop;            // 遍历所有需要视差效果的元素      elements.forEach(({ element, speed = 0.5 }) => {        // 获取元素位置信息        const rect = element.getBoundingClientRect();        // 计算偏移量 = 滚动距离 * 速度系数        const offset = scrollTop * speed;                // 应用transform实现位移效果        element.style.transform = `translateY(${offset}px)`;      });    };        // 监听滚动事件(使用passive模式提升性能)    scrollContainer.addEventListener('scroll', updateParallax, { passive: true });    // 初始化时立即执行一次    updateParallax();        // 返回清理函数,用于移除事件监听    return () => {      scrollContainer.removeEventListener('scroll', updateParallax);    };  }    // 停止所有动画  stopAll() {    // 取消所有单个动画    this.animations.forEach(animation => animation.cancel());    // 取消所有序列中的动画    this.sequences.forEach(sequence =>       sequence.forEach(animation => animation.cancel())    );        // 清空两个Map    this.animations.clear();    this.sequences.clear();  }}

6.Broadcast Channel API:替代消息通信库


无需第三方依赖实现跨标签页通信

class CrossTabCommunication {  constructor(channelName = 'app-channel') {    // 创建BroadcastChannel实例,用于跨标签页通信    this.channel = new BroadcastChannel(channelName);    // 使用Map存储消息类型与处理函数的映射关系    this.handlers = new Map();        // 监听channel的消息事件    this.channel.addEventListener('message', this.handleMessage.bind(this));        // 处理页面可见性变化,用于标签页状态同步    document.addEventListener('visibilitychange', () => {      if (document.hidden) {        // 当前标签页隐藏时广播通知        this.broadcast('tab-hidden', { tabId: this.tabId });      } else {        // 当前标签页变为可见时广播通知        this.broadcast('tab-visible', { tabId: this.tabId });      }    });        // 生成唯一的标签页ID,结合时间戳和随机字符串确保唯一性    this.tabId = `tab_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;        // 广播当前标签页的连接通知    this.broadcast('tab-connected', { tabId: this.tabId });  }    // 向所有其他标签页发送消息  broadcast(type, data = {}) {    const message = {      type,          // 消息类型      data,          // 消息数据      timestamp: Date.now(),  // 时间戳      sender: this.tabId     // 发送者标签页ID    };        this.channel.postMessage(message);  // 通过BroadcastChannel发送消息  }    // 监听特定类型的消息  on(type, handler) {    // 如果该消息类型尚未注册处理函数,初始化一个Set集合    if (!this.handlers.has(type)) {      this.handlers.set(type, new Set());    }        // 将处理函数添加到对应消息类型的集合中    this.handlers.get(type).add(handler);        // 返回取消订阅的函数,便于后续移除监听    return () => {      const handlers = this.handlers.get(type);      if (handlers) {        handlers.delete(handler);  // 移除指定处理函数        if (handlers.size === 0) {          this.handlers.delete(type);  // 如果没有处理函数了,删除该消息类型的注册        }      }    };  }    // 处理接收到的消息  handleMessage(event) {    // 从事件中解构出消息类型、数据和发送者ID    const { type, data, sender } = event.data;        // 忽略自己发送的消息,避免循环处理    if (sender === this.tabId) return;        // 获取该消息类型的所有处理函数    const handlers = this.handlers.get(type);    if (handlers) {      // 依次执行所有处理函数      handlers.forEach(handler => {        try {          handler(data, sender);  // 调用处理函数并传入数据和发送者ID        } catch (error) {          console.error('消息处理函数出错:', error);  // 捕获并打印处理函数中的错误        }      });    }  }    // 跨标签页同步状态  syncState(key, value) {    // 广播状态同步消息    this.broadcast('state-sync', { key, value });  }    // 请求其他标签页的状态  requestState(key) {    return new Promise((resolve) => {      // 设置超时处理,1秒后自动resolve为null      const timeout = setTimeout(() => {        unsubscribe();  // 取消订阅        resolve(null);  // 解析为null表示超时      }, 1000);            // 定义取消订阅函数      const unsubscribe = this.on('state-response', ({ key: responseKey, value }) => {        if (responseKey === key) {          clearTimeout(timeout);  // 收到响应后清除超时          unsubscribe();         // 取消订阅          resolve(value);        // 解析响应值        }      });            // 广播状态请求消息      this.broadcast('state-request', { key });    });  }    // 协调单标签页操作(获取锁)  async acquireLock(lockName, timeout = 5000) {    return new Promise((resolve, reject) => {      let acquired = false;  // 标记是否成功获取锁            // 设置获取锁的超时处理      const timeoutId = setTimeout(() => {        if (!acquired) {          cleanup();  // 清理监听          reject(new Error('获取锁超时'));  // 超时后拒绝Promise        }      }, timeout);            // 定义清理函数      const cleanup = this.on('lock-response', ({ lock, holder }) => {        if (lock === lockName) {          if (holder === null) {            // 如果锁未被持有,则成功获取            acquired = true;            clearTimeout(timeoutId);  // 清除超时            cleanup();               // 清理监听                        // 声明自己持有锁            this.broadcast('lock-claimed', { lock: lockName, holder: this.tabId });            // 返回释放锁的函数            resolve(() => this.releaseLock(lockName));          }        }      });            // 广播锁请求消息      this.broadcast('lock-request', { lock: lockName });    });  }    // 释放锁  releaseLock(lockName) {    this.broadcast('lock-released', { lock: lockName, holder: this.tabId });  }    // 清理资源  close() {    // 广播标签页断开连接通知    this.broadcast('tab-disconnected', { tabId: this.tabId });    // 关闭BroadcastChannel    this.channel.close();    // 清空所有消息处理函数    this.handlers.clear();  }}

7.文件系统访问API:替代文件上传库


用浏览器原生能力处理文件操作

class FileManager {  constructor() {    // 使用Map存储文件句柄,键为文件名,值为FileSystemFileHandle对象    this.fileHandles = new Map();    // 使用Map存储目录句柄,键为目录名,值为FileSystemDirectoryHandle对象    this.directoryHandles = new Map();  }    // 检查浏览器是否支持文件系统访问API  static isSupported() {    return 'showOpenFilePicker' in window &&            'showSaveFilePicker' in window &&            'showDirectoryPicker' in window;  }    // 打开单个文件选择器  async openFile(options = {}) {    try {      // 调用文件选择器API,默认配置为单选模式      const [fileHandle] = await window.showOpenFilePicker({        multiple: false,  // 禁止多选        excludeAcceptAllOption: false,  // 显示"所有文件"选项        types: [{          description: 'All files',          accept: { '*/*': [] }  // 接受所有文件类型        }],        ...options  // 合并自定义选项      });            // 获取文件对象      const file = await fileHandle.getFile();      // 存储文件句柄      this.fileHandles.set(file.name, fileHandle);            // 返回文件对象和句柄      return { file, handle: fileHandle };    } catch (error) {      // 忽略用户取消操作产生的错误      if (error.name !== 'AbortError') {        throw error;      }      return null;    }  }    // 打开多个文件选择器  async openFiles(options = {}) {    try {      // 启用多选模式      const fileHandles = await window.showOpenFilePicker({        multiple: true,        ...options      });            // 并行获取所有文件对象      const files = await Promise.all(        fileHandles.map(async (handle) => {          const file = await handle.getFile();          this.fileHandles.set(file.name, handle);          return { file, handle };        })      );            return files;    } catch (error) {      if (error.name !== 'AbortError') {        throw error;      }      return [];  // 用户取消时返回空数组    }  }    // 保存文件  async saveFile(content, options = {}) {    try {      // 调用文件保存对话框      const fileHandle = await window.showSaveFilePicker({        suggestedName: 'untitled.txt',  // 默认文件名        types: [{          description: 'Text files',          accept: { 'text/plain': ['.txt'] }  // 默认保存为文本文件        }],        ...options      });            // 获取可写流      const writable = await fileHandle.createWritable();            // 支持直接写入Blob或字符串内容      if (content instanceof Blob) {        await writable.write(content);      } else {        await writable.write(new Blob([content], { type: 'text/plain' }));      }            // 关闭流完成写入      await writable.close();            return fileHandle;    } catch (error) {      if (error.name !== 'AbortError') {        throw error;      }      return null;    }  }    // 打开目录选择器  async openDirectory(options = {}) {    try {      const directoryHandle = await window.showDirectoryPicker(options);      // 存储目录句柄      this.directoryHandles.set(directoryHandle.name, directoryHandle);            return directoryHandle;    } catch (error) {      if (error.name !== 'AbortError') {        throw error;      }      return null;    }  }    // 读取目录内容  async readDirectory(directoryHandle) {    const entries = [];        // 使用异步迭代器遍历目录项    for await (const [name, handle] of directoryHandle.entries()) {      entries.push({        name,        // 条目名称        handle,      // 文件/目录句柄        kind: handle.kind,  // 类型标识        isDirectory: handle.kind === 'directory',  // 是否为目录        isFile: handle.kind === 'file'             // 是否为文件      });    }        return entries;  }    // 在指定目录中创建文件  async createFileInDirectory(directoryHandle, fileName, content) {    try {      // 获取文件句柄(自动创建)      const fileHandle = await directoryHandle.getFileHandle(fileName, {        create: true      });            // 写入内容      const writable = await fileHandle.createWritable();      await writable.write(content);      await writable.close();            return fileHandle;    } catch (error) {      throw new Error(`创建文件失败: ${error.message}`);    }  }    // 监听文件变化(实验性功能)  async watchFile(fileHandle, callback) {    let lastModified = null;  // 记录最后修改时间        const check = async () => {      try {        const file = await fileHandle.getFile();                if (lastModified === null) {          // 首次检查,记录初始修改时间          lastModified = file.lastModified;        } else if (file.lastModified !== lastModified) {          // 检测到修改时间变化          lastModified = file.lastModified;          callback(file);  // 触发回调        }      } catch (error) {        console.error('文件监听出错:', error);      }    };        // 每秒检查一次(可根据需要调整间隔)    const interval = setInterval(check, 1000);        // 返回清理函数用于停止监听    return () => clearInterval(interval);  }    // 设置拖放区域  setupDropZone(element, options = {}) {    const handleDrop = async (event) => {      event.preventDefault();  // 阻止默认行为            // 获取拖放项并转换为文件系统句柄      const items = Array.from(event.dataTransfer.items);      const entries = [];            for (const item of items) {        if (item.kind === 'file') {          const entry = await item.getAsFileSystemHandle();          entries.push(entry);        }      }            // 触发自定义回调      if (options.onDrop) {        options.onDrop(entries);      }    };        const handleDragOver = (event) => {      event.preventDefault();  // 必须阻止默认行为才能触发drop事件      if (options.onDragOver) {        options.onDragOver(event);      }    };        // 绑定事件监听器    element.addEventListener('drop', handleDrop);    element.addEventListener('dragover', handleDragOver);        // 返回清理函数用于移除监听    return () => {      element.removeEventListener('drop', handleDrop);      element.removeEventListener('dragover', handleDragOver);    };  }}

8.支付请求API:替代支付处理库


原生处理支付流程

class PaymentManager {  constructor() {    // 初始化支持的支付方式(基础信用卡支付)    this.supportedMethods = [      {        supportedMethods: 'basic-card',  // 支付方式标识        data: {          supportedNetworks: ['visa', 'mastercard', 'amex'],  // 支持的卡组织          supportedTypes: ['debit', 'credit']  // 支持借记卡/信用卡        }      }    ];  }    // 检查浏览器是否支持支付请求API  static isSupported() {    return 'PaymentRequest' in window;  }    // 创建支付请求  async createPaymentRequest(details, options = {}) {    if (!PaymentManager.isSupported()) {      throw new Error('当前浏览器不支持支付请求API');    }        // 默认支付详情(会被传入的details参数覆盖)    const paymentDetails = {      total: {        label: '总计',  // 总金额标签        amount: { currency: 'USD', value: '0.00' }  // 默认美元货币      },      displayItems: [],  // 显示项目列表      ...details  // 合并自定义支付详情    };        // 默认支付选项(会被传入的options参数覆盖)    const paymentOptions = {      requestPayerName: true,    // 要求提供付款人姓名      requestPayerEmail: true,   // 要求提供付款人邮箱      requestPayerPhone: false,  // 不要求提供电话      requestShipping: false,    // 不要求配送地址      ...options  // 合并自定义选项    };        // 创建并返回PaymentRequest实例    return new PaymentRequest(      this.supportedMethods,  // 支持的支付方式      paymentDetails,         // 支付详情      paymentOptions          // 支付选项    );  }    // 处理支付流程  async processPayment(details, options = {}) {    try {      // 创建支付请求      const request = await this.createPaymentRequest(details, options);            // 检查用户是否可以使用当前支付方式      const canMakePayment = await request.canMakePayment();      if (!canMakePayment) {        throw new Error('用户无法使用可用支付方式进行付款');      }            // 显示支付界面并获取用户响应      const response = await request.show();            // 验证支付信息(需实现自定义验证逻辑)      const isValid = await this.validatePayment(response);            if (isValid) {        // 支付成功,完成交易        await response.complete('success');        return {          success: true,          response,  // 支付响应对象          paymentMethod: response.methodName,  // 使用的支付方式          details: response.details  // 支付详情        };      } else {        // 支付验证失败        await response.complete('fail');        return {          success: false,          error: '支付验证失败'        };      }          } catch (error) {      // 捕获并返回错误信息      return {        success: false,        error: error.message      };    }  }    // 更新配送选项(根据地址变化动态计算)  updateShippingOptions(request, shippingOptions) {    // 监听配送地址变化事件    request.addEventListener('shippingaddresschange', async (event) => {      const shippingAddress = request.shippingAddress;      // 根据地址计算配送选项      const options = await this.calculateShippingOptions(shippingAddress);            // 更新支付请求的配送选项      event.updateWith({        shippingOptions: options      });    });  }    // 计算配送选项和费用  async calculateShippingOptions(address) {    // 实现您的配送计算逻辑    // 基础配送选项    const baseShipping = {       id: 'standard',         // 选项ID      label: '标准配送',      // 显示标签      amount: { currency: 'USD', value: '5.00' }  // 配送费用    };        // 加急配送选项    const expressShipping = {       id: 'express',          // 选项ID      label: '加急配送',      // 显示标签      amount: { currency: 'USD', value: '15.00' }  // 配送费用    };        return [baseShipping, expressShipping];  }    // 验证支付信息(通常需要服务器端验证)  async validatePayment(response) {    // 实现您的支付验证逻辑    // 这里示例调用服务器API进行验证        try {      const validationResult = await fetch('/api/validate-payment', {        method: 'POST',        headers: { 'Content-Type': 'application/json' },        body: JSON.stringify({          methodName: response.methodName,  // 支付方式          details: response.details,        // 支付详情          payerName: response.payerName,    // 付款人姓名          payerEmail: response.payerEmail   // 付款人邮箱        })      });            // 返回验证结果(根据API响应状态)      return validationResult.ok;    } catch (error) {      console.error('支付验证错误:', error);      return false;    }  }    // 处理订阅支付  async createSubscription(planDetails, options = {}) {    // 构建订阅支付详情    const subscriptionDetails = {      total: {        label: `${planDetails.name} - 月付`,  // 显示标签        amount: { currency: 'USD', value: planDetails.price }  // 订阅金额      },      displayItems: [        {          label: planDetails.name,  // 订阅计划名称          amount: { currency: 'USD', value: planDetails.price }  // 计划价格        }      ]    };        // 处理支付(强制要求提供邮箱)    const result = await this.processPayment(subscriptionDetails, {      ...options,      requestPayerEmail: true    });        // 支付成功后存储订阅信息    if (result.success) {      await this.storeSubscription({        planId: planDetails.id,            // 计划ID        payerEmail: result.response.payerEmail,  // 付款人邮箱        paymentMethod: result.paymentMethod  // 支付方式      });    }        return result;  }    // 存储订阅信息到服务器  async storeSubscription(subscriptionData) {    // 实现您的订阅存储逻辑    return fetch('/api/subscriptions', {      method: 'POST',      headers: { 'Content-Type': 'application/json' },      body: JSON.stringify(subscriptionData)  // 订阅数据    });  }}

9.屏幕捕捉API:替代屏幕录制库


原生实现屏幕内容捕获

class ScreenCaptureManager {  constructor() {    this.mediaRecorder = null;    // 用于存储媒体录制器实例    this.recordedChunks = [];    // 存储录制视频的数据块    this.stream = null;          // 存储屏幕捕获的媒体流  }    // 检查浏览器是否支持屏幕捕获API  static isSupported() {    return 'getDisplayMedia' in navigator.mediaDevices;  }    // 开始屏幕捕获  async startCapture(options = {}) {    if (!ScreenCaptureManager.isSupported()) {      throw new Error('当前浏览器不支持屏幕捕获API');    }        try {      // 配置屏幕捕获选项      const displayMediaOptions = {        video: {          cursor: 'always',        // 始终显示鼠标光标          displaySurface: 'browser', // 默认捕获浏览器窗口          ...options.video         // 合并自定义视频选项        },        audio: {          echoCancellation: true,  // 启用回声消除          noiseSuppression: true,  // 启用噪音抑制          sampleRate: 44100,       // 设置音频采样率          ...options.audio         // 合并自定义音频选项        }      };            // 获取屏幕共享媒体流      this.stream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);            // 监听视频轨道结束事件(用户停止共享时触发)      this.stream.getVideoTracks()[0].addEventListener('ended', () => {        this.stopCapture();      });            return this.stream;    } catch (error) {      throw new Error(`屏幕捕获启动失败: ${error.message}`);    }  }    // 开始录制屏幕  startRecording(options = {}) {    if (!this.stream) {      throw new Error('没有活动的屏幕捕获流');    }        // 配置录制选项    const recordingOptions = {      mimeType: 'video/webm;codecs=vp9',  // 默认使用VP9编码      videoBitsPerSecond: 2500000,        // 设置视频比特率      ...options                          // 合并自定义选项    };        // 支持的视频格式列表    const supportedTypes = [      'video/webm;codecs=vp9',      'video/webm;codecs=vp8',      'video/webm',      'video/mp4'    ];        // 检测浏览器实际支持的视频格式    const mimeType = supportedTypes.find(type =>       MediaRecorder.isTypeSupported(type)    ) || 'video/webm';  // 默认回退到webm格式        // 创建媒体录制器实例    this.mediaRecorder = new MediaRecorder(this.stream, {      ...recordingOptions,      mimeType  // 使用检测到的支持格式    });        // 初始化录制数据块数组    this.recordedChunks = [];        // 数据可用事件处理    this.mediaRecorder.ondataavailable = (event) => {      if (event.data.size > 0) {        this.recordedChunks.push(event.data);  // 存储视频数据块      }    };        // 录制停止事件处理    this.mediaRecorder.onstop = () => {      // 将所有数据块合并为一个Blob对象      const blob = new Blob(this.recordedChunks, { type: mimeType });      this.onRecordingComplete(blob);  // 处理录制完成的视频    };        // 开始录制,每1000毫秒收集一次数据    this.mediaRecorder.start(1000);        return this.mediaRecorder;  }    // 停止录制  stopRecording() {    // 如果录制器存在且处于活动状态    if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {      this.mediaRecorder.stop();  // 停止录制    }  }    // 停止屏幕捕获  stopCapture() {    // 停止所有媒体轨道    if (this.stream) {      this.stream.getTracks().forEach(track => track.stop());      this.stream = null;    }        // 停止录制器    if (this.mediaRecorder) {      this.stopRecording();    }  }    // 处理录制完成的视频  onRecordingComplete(blob) {    // 创建视频下载链接    const url = URL.createObjectURL(blob);    const a = document.createElement('a');    a.style.display = 'none';  // 隐藏下载链接    a.href = url;    a.download = `screen-recording-${Date.now()}.webm`;  // 设置带时间戳的文件名    document.body.appendChild(a);    a.click();  // 触发下载        // 清理DOM和释放内存    setTimeout(() => {      document.body.removeChild(a);      URL.revokeObjectURL(url);  // 释放对象URL    }, 100);  }    // 截取屏幕画面  async takeScreenshot() {    // 如果没有活动流,则自动启动无音频的屏幕捕获    if (!this.stream) {      await this.startCapture({ audio: false });    }        // 创建视频元素用于捕获画面    const video = document.createElement('video');    video.srcObject = this.stream;    video.play();  // 必须调用play()才能获取有效画面        return new Promise((resolve) => {      // 等待视频元数据加载完成      video.addEventListener('loadedmetadata', () => {        // 创建画布用于截图        const canvas = document.createElement('canvas');        const ctx = canvas.getContext('2d');                // 设置画布尺寸与视频一致        canvas.width = video.videoWidth;        canvas.height = video.videoHeight;                // 将视频画面绘制到画布        ctx.drawImage(video, 0, 0);                // 将画布转换为PNG格式的Blob对象        canvas.toBlob((blob) => {          resolve(blob);  // 返回截图数据        }, 'image/png');      });    });  }    // 创建实时预览窗口  createPreview(container) {    if (!this.stream) {      throw new Error('没有活动的屏幕捕获流');    }        // 创建并配置预览视频元素    const video = document.createElement('video');    video.srcObject = this.stream;    video.autoplay = true;  // 自动播放    video.muted = true;     // 静音避免回声    video.style.width = '100%';    video.style.height = 'auto';        // 将预览添加到指定容器    container.appendChild(video);        return video;  }    // 启用画中画模式  async enablePictureInPicture(video) {    // 检查浏览器是否支持画中画    if ('pictureInPictureEnabled' in document) {      try {        await video.requestPictureInPicture();  // 请求进入画中画模式      } catch (error) {        console.error('启用画中画失败:', error);      }    }  }}

10.地理定位API:替代位置服务库


原生获取设备位置信息

class LocationManager {  constructor(options = {}) {    // 默认配置选项(可被传入的options覆盖)    this.options = {      enableHighAccuracy: true,  // 启用高精度定位      timeout: 10000,           // 超时时间(10秒)      maximumAge: 300000,       // 位置缓存有效期(5分钟)      ...options                // 合并自定义配置    };        this.watchId = null;            // 位置监听器ID    this.lastKnownPosition = null;  // 最后已知位置    this.listeners = new Set();     // 使用Set存储监听回调函数  }    // 检查浏览器是否支持地理定位API  static isSupported() {    return 'geolocation' in navigator;  }    // 获取当前位置(单次请求)  async getCurrentPosition(options = {}) {    if (!LocationManager.isSupported()) {      throw new Error('浏览器不支持地理定位功能');    }        return new Promise((resolve, reject) => {      navigator.geolocation.getCurrentPosition(        (position) => {          this.lastKnownPosition = position;  // 缓存最新位置          resolve(this.formatPosition(position));  // 返回格式化后的位置数据        },        (error) => {          reject(this.handleError(error));  // 处理并返回错误信息        },        { ...this.options, ...options }  // 合并配置选项      );    });  }    // 持续监听位置变化  startWatching(callback, options = {}) {    if (!LocationManager.isSupported()) {      throw new Error('浏览器不支持地理定位功能');    }        // 添加回调到监听器集合    this.listeners.add(callback);        // 如果尚未启动监听,则创建监听器    if (!this.watchId) {      this.watchId = navigator.geolocation.watchPosition(        (position) => {          this.lastKnownPosition = position;  // 更新最后已知位置          const formattedPosition = this.formatPosition(position);  // 格式化位置数据                    // 通知所有监听者          this.listeners.forEach(listener => {            try {              listener(formattedPosition);            } catch (error) {              console.error('位置监听回调出错:', error);            }          });        },        (error) => {          const formattedError = this.handleError(error);  // 格式化错误信息                    // 通知所有监听者(携带错误)          this.listeners.forEach(listener => {            try {              listener(null, formattedError);            } catch (error) {              console.error('错误监听回调出错:', error);            }          });        },        { ...this.options, ...options }  // 合并配置选项      );    }        // 返回取消监听的函数    return () => {      this.listeners.delete(callback);  // 移除当前回调            // 如果没有监听者了,停止位置监听      if (this.listeners.size === 0) {        this.stopWatching();      }    };  }    // 停止监听位置变化  stopWatching() {    if (this.watchId) {      navigator.geolocation.clearWatch(this.watchId);  // 清除监听器      this.watchId = null;    }    this.listeners.clear();  // 清空所有监听回调  }    // 格式化位置数据为简洁对象  formatPosition(position) {    return {      latitude: position.coords.latitude,          // 纬度      longitude: position.coords.longitude,        // 经度      accuracy: position.coords.accuracy,          // 水平精度(米)      altitude: position.coords.altitude,          // 海拔高度      altitudeAccuracy: position.coords.altitudeAccuracy,  // 高度精度      heading: position.coords.heading,             // 行进方向(度)      speed: position.coords.speed,                // 速度(米/秒)      timestamp: position.timestamp                // 时间戳    };  }    // 处理地理定位错误  handleError(error) {    const errorMessages = {      1: '用户拒绝位置访问权限',      // PERMISSION_DENIED      2: '无法获取位置信息',         // POSITION_UNAVAILABLE      3: '位置请求超时'             // TIMEOUT    };        return {      code: error.code,      message: errorMessages[error.code] || '未知位置错误'    };  }    // 计算两点间距离(使用Haversine公式)  calculateDistance(pos1, pos2) {    const R = 6371;  // 地球半径(公里)    const dLat = this.toRadians(pos2.latitude - pos1.latitude);  // 纬度差转弧度    const dLon = this.toRadians(pos2.longitude - pos1.longitude); // 经度差转弧度        // Haversine公式计算    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +              Math.cos(this.toRadians(pos1.latitude)) *               Math.cos(this.toRadians(pos2.latitude)) *              Math.sin(dLon / 2) * Math.sin(dLon / 2);        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));    return R * c;  // 返回距离(公里)  }    // 角度转弧度  toRadians(degrees) {    return degrees * (Math.PI / 180);  }    // 反向地理编码(需要OpenCageData服务)  async reverseGeocode(latitude, longitude) {    try {      const response = await fetch(        `https://api.opencagedata.com/geocode/v1/json?q=${latitude}+${longitude}&key=YOUR_API_KEY`      );            const data = await response.json();            if (data.results && data.results.length > 0) {        return {          address: data.results[0].formatted,    // 格式化地址          components: data.results[0].components // 地址组成部分        };      }            return null;  // 无结果    } catch (error) {      console.error('反向地理编码失败:', error);      return null;    }  }    // 设置地理围栏(进入/离开指定区域触发回调)  async setupGeofence(center, radius, callback) {    // 启动位置监听    const unsubscribe = this.startWatching((position, error) => {      if (error) return;  // 忽略错误情况            // 计算当前位置与围栏中心的距离      const distance = this.calculateDistance(center, position);      const isInside = distance <= radius;  // 是否在围栏内            // 触发回调      callback({        position,    // 当前位置        distance,    // 与围栏中心的距离        isInside,    // 是否在围栏内        center,      // 围栏中心点        radius       // 围栏半径      });    });        // 返回取消监听的函数    return unsubscribe;  }}

实施策略与性能优势

这些原生Web API相比第三方库具有显著优势:

代码体积缩减

  • Web Components:比React小80-90%
  • Fetch API:比Axios小95%
  • Web Animations:基础动画比GSAP小70%

性能提升

  • 零依赖包开销
  • 直接浏览器级优化
  • 更低内存占用
  • 更快执行速度

维护优势

  • 无需依赖更新
  • 自动随浏览器升级
  • 减少安全漏洞
  • 更好的长期稳定性

浏览器支持考量

  • 多数API支持率超过90%
  • 支持渐进增强
  • 旧版浏览器可提供polyfill

利用这些web API, 让你未来的应用实现更小的JavaScript包体积、更优的性能表现和更强的未来适应性。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有

相关阅读