🚀 应用出了问题你都不知道,别着急我来帮你

🎯 学习目标:掌握前端监控体系的核心指标和搭建方法,让线上问题无处遁形

📊 难度等级 :中级-高级

🏷️ 技术标签#前端监控 #性能指标 #用户体验 #错误监控

⏱️ 阅读时间:约8分钟


🌟 引言

在日常的前端开发中,你是否遇到过这样的困扰:

  • 线上问题难发现:用户反馈问题时,开发者才知道出了bug
  • 性能问题无法量化:不知道页面加载到底有多慢,用户体验如何
  • 错误排查困难:线上报错信息不完整,无法快速定位问题
  • 用户行为不透明:不了解用户真实的使用情况和痛点

今天分享5个前端监控体系的关键指标和搭建方法,让你的应用问题无处遁形,用户体验持续优化!


💡 核心技巧详解

1. Core Web Vitals监控:用户体验的量化标准

🔍 应用场景

谷歌推出的Core Web Vitals是衡量用户体验的核心指标,包括LCP、FID、CLS三个关键指标。

❌ 常见问题

很多开发者只关注功能实现,忽略了用户体验的量化监控。

javascript 复制代码
// ❌ 传统做法:没有性能监控
window.onload = () => {
  console.log('页面加载完成');
};

✅ 推荐方案

使用Web Vitals库监控核心性能指标。

javascript 复制代码
/**
 * Core Web Vitals监控实现
 * @description 监控LCP、FID、CLS三个核心指标
 * @param {Function} onPerfEntry - 性能数据回调函数
 */
const monitorWebVitals = (onPerfEntry) => {
  // 动态导入web-vitals库
  import('web-vitals').then(({ getCLS, getINP, getLCP }) => {
    // 监控Cumulative Layout Shift (布局偏移)
    getCLS(onPerfEntry);
    // 监控Interaction to Next Paint (交互到下次绘制)
    getINP(onPerfEntry);
    // 监控Largest Contentful Paint (最大内容绘制)
    getLCP(onPerfEntry);
  });
};

/**
 * 性能数据处理函数
 * @description 处理并上报性能指标数据
 * @param {Object} metric - 性能指标对象
 */
const handlePerfEntry = (metric) => {
  const { name, value, id } = metric;
  
  // 发送性能数据到监控平台
  sendToAnalytics({
    metric_name: name,
    metric_value: value,
    metric_id: id,
    timestamp: Date.now(),
    page_url: window.location.href
  });
};

// 启动监控
monitorWebVitals(handlePerfEntry);

💡 核心要点

  • LCP < 2.5s:最大内容绘制时间,衡量加载性能
  • INP < 200ms:交互到下次绘制,衡量交互性能(2024年3月已替代FID)
  • CLS < 0.1:累积布局偏移,衡量视觉稳定性

🎯 实际应用

在Vue3项目中集成性能监控:

javascript 复制代码
// 在main.js中集成
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// 应用挂载后启动监控
app.mount('#app');
monitorWebVitals(handlePerfEntry);

2. JavaScript错误监控:异常捕获与上报系统

🔍 应用场景

捕获并上报JavaScript运行时错误、Promise异常、资源加载失败等问题。

❌ 常见问题

只在开发环境处理错误,线上错误无法及时发现和处理。

javascript 复制代码
// ❌ 传统做法:只有console.error
try {
  // 业务代码
} catch (error) {
  console.error('出错了:', error);
}

✅ 推荐方案

建立完整的错误监控体系。

javascript 复制代码
/**
 * 全局错误监控系统
 * @description 捕获并上报各种类型的JavaScript错误
 */
class ErrorMonitor {
  constructor() {
    this.init();
  }

  /**
   * 初始化错误监控
   * @description 设置全局错误监听器
   */
  init = () => {
    // 监听JavaScript运行时错误
    window.addEventListener('error', this.handleError);
    
    // 监听Promise未捕获的异常
    window.addEventListener('unhandledrejection', this.handlePromiseError);
    
    // 监听资源加载错误
    window.addEventListener('error', this.handleResourceError, true);
  };

  /**
   * 处理JavaScript错误
   * @description 处理运行时错误并上报
   * @param {ErrorEvent} event - 错误事件对象
   */
  handleError = (event) => {
    const errorInfo = {
      type: 'javascript_error',
      message: event.message,
      filename: event.filename,
      lineno: event.lineno,
      colno: event.colno,
      stack: event.error?.stack,
      timestamp: Date.now(),
      user_agent: navigator.userAgent,
      page_url: window.location.href
    };
    
    this.reportError(errorInfo);
  };

  /**
   * 处理Promise异常
   * @description 处理未捕获的Promise异常
   * @param {PromiseRejectionEvent} event - Promise异常事件
   */
  handlePromiseError = (event) => {
    const errorInfo = {
      type: 'promise_error',
      message: event.reason?.message || event.reason,
      stack: event.reason?.stack,
      timestamp: Date.now(),
      page_url: window.location.href
    };
    
    this.reportError(errorInfo);
  };

  /**
   * 上报错误信息
   * @description 将错误信息发送到监控平台
   * @param {Object} errorInfo - 错误信息对象
   */
  reportError = (errorInfo) => {
    // 使用beacon API确保数据发送
    if (navigator.sendBeacon) {
      navigator.sendBeacon('/api/error-report', JSON.stringify(errorInfo));
    } else {
      fetch('/api/error-report', {
        method: 'POST',
        body: JSON.stringify(errorInfo),
        headers: { 'Content-Type': 'application/json' }
      }).catch(() => {});
    }
  };
}

// 启动错误监控
const errorMonitor = new ErrorMonitor();

💡 核心要点

  • 全局捕获:监听window的error和unhandledrejection事件
  • 详细信息:收集错误堆栈、文件位置、用户环境等信息
  • 可靠上报:使用sendBeacon API确保数据发送成功

🎯 实际应用

Vue3项目中的错误边界处理:

javascript 复制代码
// Vue3全局错误处理
app.config.errorHandler = (err, vm, info) => {
  const errorInfo = {
    type: 'vue_error',
    message: err.message,
    stack: err.stack,
    component_info: info,
    timestamp: Date.now()
  };
  
  errorMonitor.reportError(errorInfo);
};

3. 用户行为分析:页面访问与操作轨迹追踪

🔍 应用场景

追踪用户的页面访问路径、点击行为、停留时间等,分析用户使用习惯。

❌ 常见问题

缺乏用户行为数据,无法了解用户真实的使用情况和痛点。

javascript 复制代码
// ❌ 传统做法:没有用户行为追踪
button.addEventListener('click', () => {
  // 只处理业务逻辑
  handleButtonClick();
});

✅ 推荐方案

建立用户行为追踪系统。

javascript 复制代码
/**
 * 用户行为追踪系统
 * @description 追踪用户的各种操作行为
 */
class UserBehaviorTracker {
  constructor() {
    this.sessionId = this.generateSessionId();
    this.init();
  }

  /**
   * 生成会话ID
   * @description 为每个用户会话生成唯一标识
   * @returns {string} 会话ID
   */
  generateSessionId = () => {
    return Date.now().toString(36) + Math.random().toString(36).substr(2);
  };

  /**
   * 初始化行为追踪
   * @description 设置各种行为监听器
   */
  init = () => {
    // 页面访问追踪
    this.trackPageView();
    
    // 点击行为追踪
    this.trackClicks();
    
    // 页面停留时间追踪
    this.trackPageDuration();
    
    // 滚动行为追踪
    this.trackScrollBehavior();
  };

  /**
   * 追踪页面访问
   * @description 记录用户访问的页面信息
   */
  trackPageView = () => {
    const pageInfo = {
      type: 'page_view',
      session_id: this.sessionId,
      page_url: window.location.href,
      page_title: document.title,
      referrer: document.referrer,
      timestamp: Date.now(),
      screen_resolution: `${screen.width}x${screen.height}`,
      viewport_size: `${window.innerWidth}x${window.innerHeight}`
    };
    
    this.sendTrackingData(pageInfo);
  };

  /**
   * 追踪点击行为
   * @description 监听并记录用户的点击操作
   */
  trackClicks = () => {
    document.addEventListener('click', (event) => {
      const target = event.target;
      const clickInfo = {
        type: 'click',
        session_id: this.sessionId,
        element_tag: target.tagName.toLowerCase(),
        element_id: target.id,
        element_class: target.className,
        element_text: target.textContent?.slice(0, 100),
        page_url: window.location.href,
        timestamp: Date.now(),
        coordinates: { x: event.clientX, y: event.clientY }
      };
      
      this.sendTrackingData(clickInfo);
    });
  };

  /**
   * 追踪页面停留时间
   * @description 计算用户在页面的停留时间
   */
  trackPageDuration = () => {
    const startTime = Date.now();
    
    // 页面卸载时发送停留时间
    window.addEventListener('beforeunload', () => {
      const duration = Date.now() - startTime;
      const durationInfo = {
        type: 'page_duration',
        session_id: this.sessionId,
        page_url: window.location.href,
        duration: duration,
        timestamp: Date.now()
      };
      
      // 使用sendBeacon确保数据发送
      navigator.sendBeacon('/api/behavior-track', JSON.stringify(durationInfo));
    });
  };

  /**
   * 发送追踪数据
   * @description 将行为数据发送到分析平台
   * @param {Object} data - 追踪数据对象
   */
  sendTrackingData = (data) => {
    fetch('/api/behavior-track', {
      method: 'POST',
      body: JSON.stringify(data),
      headers: { 'Content-Type': 'application/json' }
    }).catch(() => {});
  };
}

// 启动用户行为追踪
const behaviorTracker = new UserBehaviorTracker();

💡 核心要点

  • 会话管理:为每个用户会话生成唯一标识
  • 多维度追踪:页面访问、点击、停留时间、滚动等
  • 隐私保护:只收集必要的行为数据,保护用户隐私

🎯 实际应用

在Vue3路由中集成行为追踪:

javascript 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),
  routes: [...]
});

// 路由变化时追踪页面访问
router.afterEach((to, from) => {
  behaviorTracker.trackPageView();
});

4. 性能监控:资源加载与接口响应时间分析

🔍 应用场景

监控页面资源加载时间、API接口响应时间、网络状况等性能指标。

❌ 常见问题

缺乏性能数据支撑,无法识别性能瓶颈和优化方向。

javascript 复制代码
// ❌ 传统做法:没有性能监控
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    // 处理数据
  });

✅ 推荐方案

建立全面的性能监控体系。

javascript 复制代码
/**
 * 性能监控系统
 * @description 监控资源加载和接口性能
 */
class PerformanceMonitor {
  constructor() {
    this.init();
  }

  /**
   * 初始化性能监控
   * @description 设置各种性能监听器
   */
  init = () => {
    // 监控资源加载性能
    this.monitorResourceLoading();
    
    // 监控导航性能
    this.monitorNavigationTiming();
    
    // 拦截fetch请求监控API性能
    this.interceptFetch();
  };

  /**
   * 监控资源加载性能
   * @description 监控CSS、JS、图片等资源的加载时间
   */
  monitorResourceLoading = () => {
    // 使用PerformanceObserver监控资源
    const observer = new PerformanceObserver((list) => {
      list.getEntries().forEach((entry) => {
        if (entry.entryType === 'resource') {
          const resourceInfo = {
            type: 'resource_timing',
            name: entry.name,
            duration: entry.duration,
            size: entry.transferSize,
            start_time: entry.startTime,
            response_end: entry.responseEnd,
            timestamp: Date.now()
          };
          
          this.sendPerformanceData(resourceInfo);
        }
      });
    });
    
    observer.observe({ entryTypes: ['resource'] });
  };

  /**
   * 监控导航性能
   * @description 监控页面导航的各个阶段耗时
   */
  monitorNavigationTiming = () => {
    window.addEventListener('load', () => {
      setTimeout(() => {
        const navigation = performance.getEntriesByType('navigation')[0];
        
        const timingInfo = {
          type: 'navigation_timing',
          dns_lookup: navigation.domainLookupEnd - navigation.domainLookupStart,
          tcp_connect: navigation.connectEnd - navigation.connectStart,
          request_response: navigation.responseEnd - navigation.requestStart,
          dom_parse: navigation.domContentLoadedEventEnd - navigation.responseEnd,
          resource_load: navigation.loadEventEnd - navigation.domContentLoadedEventEnd,
          total_time: navigation.loadEventEnd - navigation.navigationStart,
          timestamp: Date.now()
        };
        
        this.sendPerformanceData(timingInfo);
      }, 0);
    });
  };

  /**
   * 拦截fetch请求监控API性能
   * @description 监控API接口的响应时间和状态
   */
  interceptFetch = () => {
    const originalFetch = window.fetch;
    
    window.fetch = async (...args) => {
      const startTime = performance.now();
      const url = args[0];
      
      try {
        const response = await originalFetch(...args);
        const endTime = performance.now();
        
        // 记录API性能数据
        const apiInfo = {
          type: 'api_timing',
          url: url,
          method: args[1]?.method || 'GET',
          status: response.status,
          duration: endTime - startTime,
          success: response.ok,
          timestamp: Date.now()
        };
        
        this.sendPerformanceData(apiInfo);
        return response;
      } catch (error) {
        const endTime = performance.now();
        
        // 记录API错误
        const errorInfo = {
          type: 'api_error',
          url: url,
          method: args[1]?.method || 'GET',
          duration: endTime - startTime,
          error: error.message,
          timestamp: Date.now()
        };
        
        this.sendPerformanceData(errorInfo);
        throw error;
      }
    };
  };

  /**
   * 发送性能数据
   * @description 将性能数据发送到监控平台
   * @param {Object} data - 性能数据对象
   */
  sendPerformanceData = (data) => {
    fetch('/api/performance-monitor', {
      method: 'POST',
      body: JSON.stringify(data),
      headers: { 'Content-Type': 'application/json' }
    }).catch(() => {});
  };
}

// 启动性能监控
const performanceMonitor = new PerformanceMonitor();

💡 核心要点

  • PerformanceObserver:使用现代API监控性能指标
  • Navigation Timing:分析页面加载各阶段的耗时
  • Resource Timing:监控资源加载性能
  • API拦截:监控接口响应时间和成功率

🎯 实际应用

在Vue3项目中集成性能监控:

javascript 复制代码
// 在axios拦截器中集成API监控
import axios from 'axios';

axios.interceptors.request.use((config) => {
  config.startTime = performance.now();
  return config;
});

axios.interceptors.response.use(
  (response) => {
    const duration = performance.now() - response.config.startTime;
    // 发送性能数据
    performanceMonitor.sendPerformanceData({
      type: 'axios_timing',
      url: response.config.url,
      duration: duration,
      status: response.status
    });
    return response;
  },
  (error) => {
    // 处理错误情况
    return Promise.reject(error);
  }
);

5. 监控平台搭建:从数据收集到可视化展示

🔍 应用场景

搭建完整的监控平台,实现数据收集、存储、分析和可视化展示。

❌ 常见问题

数据收集了但没有有效的分析和展示,无法发挥监控数据的价值。

javascript 复制代码
// ❌ 传统做法:数据只是简单存储
app.post('/api/monitor', (req, res) => {
  console.log(req.body); // 只是打印日志
  res.json({ success: true });
});

✅ 推荐方案

建立完整的监控数据处理流程。

javascript 复制代码
/**
 * 监控数据处理中心
 * @description 处理、分析和存储监控数据
 */
class MonitoringDataProcessor {
  constructor() {
    this.alertRules = new Map();
    this.init();
  }

  /**
   * 初始化监控数据处理
   * @description 设置告警规则和数据处理流程
   */
  init = () => {
    // 设置告警规则
    this.setupAlertRules();
  };

  /**
   * 设置告警规则
   * @description 定义各种监控指标的告警阈值
   */
  setupAlertRules = () => {
    // 性能告警规则
    this.alertRules.set('lcp_threshold', { value: 2500, type: 'performance' });
    this.alertRules.set('inp_threshold', { value: 200, type: 'performance' });
    this.alertRules.set('cls_threshold', { value: 0.1, type: 'performance' });
    
    // 错误率告警规则
    this.alertRules.set('error_rate', { value: 0.05, type: 'error' });
    
    // API响应时间告警
    this.alertRules.set('api_response_time', { value: 3000, type: 'api' });
  };

  /**
   * 处理监控数据
   * @description 分析监控数据并触发相应的处理逻辑
   * @param {Object} data - 监控数据对象
   */
  processMonitoringData = (data) => {
    try {
      // 数据验证和清洗
      const cleanedData = this.cleanData(data);
      
      // 存储数据
      this.storeData(cleanedData);
      
      // 实时分析
      this.analyzeData(cleanedData);
      
      // 检查告警条件
      this.checkAlerts(cleanedData);
      
    } catch (error) {
      console.error('监控数据处理失败:', error);
    }
  };

  /**
   * 数据清洗
   * @description 验证和清洗监控数据
   * @param {Object} data - 原始监控数据
   * @returns {Object} 清洗后的数据
   */
  cleanData = (data) => {
    return {
      ...data,
      timestamp: data.timestamp || Date.now(),
      session_id: data.session_id || 'unknown',
      user_agent: data.user_agent || 'unknown',
      page_url: this.sanitizeUrl(data.page_url)
    };
  };

  /**
   * 存储监控数据
   * @description 将数据存储到数据库
   * @param {Object} data - 清洗后的监控数据
   */
  storeData = async (data) => {
    // 根据数据类型存储到不同的表
    const tableName = this.getTableName(data.type);
    
    // 这里可以使用数据库ORM或直接SQL
    await this.database.insert(tableName, data);
  };

  /**
   * 实时数据分析
   * @description 对监控数据进行实时分析
   * @param {Object} data - 监控数据
   */
  analyzeData = (data) => {
    switch (data.type) {
      case 'performance':
        this.analyzePerformance(data);
        break;
      case 'error':
        this.analyzeError(data);
        break;
      case 'user_behavior':
        this.analyzeUserBehavior(data);
        break;
    }
  };

  /**
   * 检查告警条件
   * @description 根据告警规则检查是否需要发送告警
   * @param {Object} data - 监控数据
   */
  checkAlerts = (data) => {
    // 检查性能指标告警
    if (data.type === 'performance') {
      if (data.metric_name === 'LCP' && data.metric_value > 2500) {
        this.sendAlert({
          type: 'performance_alert',
          message: `LCP指标异常: ${data.metric_value}ms`,
          severity: 'warning',
          data: data
        });
      }
    }
    
    // 检查错误率告警
    if (data.type === 'error') {
      this.checkErrorRate(data);
    }
  };

  /**
   * 发送告警
   * @description 发送告警通知
   * @param {Object} alert - 告警信息
   */
  sendAlert = (alert) => {
    // 发送到钉钉、企业微信等
    this.sendToWebhook(alert);
    
    // 发送邮件通知
    this.sendEmailAlert(alert);
    
    // 记录告警日志
    console.log('告警触发:', alert);
  };

  /**
   * 获取监控仪表板数据
   * @description 为前端仪表板提供数据
   * @param {Object} params - 查询参数
   * @returns {Object} 仪表板数据
   */
  getDashboardData = async (params) => {
    const { timeRange, metrics } = params;
    
    // 查询性能指标
    const performanceData = await this.getPerformanceMetrics(timeRange);
    
    // 查询错误统计
    const errorData = await this.getErrorStatistics(timeRange);
    
    // 查询用户行为数据
    const behaviorData = await this.getUserBehaviorData(timeRange);
    
    return {
      performance: performanceData,
      errors: errorData,
      behavior: behaviorData,
      timestamp: Date.now()
    };
  };
}

// 创建监控数据处理实例
const dataProcessor = new MonitoringDataProcessor();

// Express路由处理
app.post('/api/monitor', (req, res) => {
  dataProcessor.processMonitoringData(req.body);
  res.json({ success: true });
});

// 仪表板数据接口
app.get('/api/dashboard', async (req, res) => {
  const data = await dataProcessor.getDashboardData(req.query);
  res.json(data);
});

💡 核心要点

  • 数据处理流程:收集→清洗→存储→分析→告警
  • 告警机制:设置合理的阈值和告警规则
  • 可视化展示:提供直观的监控仪表板
  • 实时性:支持实时数据分析和告警

🎯 实际应用

Vue3监控仪表板组件:

vue 复制代码
<template>
  <div class="monitoring-dashboard">
    <div class="metrics-grid">
      <MetricCard 
        title="Core Web Vitals"
        :data="performanceMetrics"
      />
      <MetricCard 
        title="错误统计"
        :data="errorMetrics"
      />
      <MetricCard 
        title="用户行为"
        :data="behaviorMetrics"
      />
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { fetchDashboardData } from '@/api/monitoring';

const performanceMetrics = ref({});
const errorMetrics = ref({});
const behaviorMetrics = ref({});

/**
 * 加载仪表板数据
 * @description 从API获取监控数据并更新界面
 */
const loadDashboardData = async () => {
  try {
    const data = await fetchDashboardData({
      timeRange: '24h',
      metrics: ['performance', 'errors', 'behavior']
    });
    
    performanceMetrics.value = data.performance;
    errorMetrics.value = data.errors;
    behaviorMetrics.value = data.behavior;
  } catch (error) {
    console.error('加载监控数据失败:', error);
  }
};

onMounted(() => {
  loadDashboardData();
  
  // 定时刷新数据
  setInterval(loadDashboardData, 30000);
});
</script>

📊 技巧对比总结

监控类型 使用场景 优势 注意事项
Core Web Vitals 用户体验量化 标准化指标,直观反映体验 需要持续优化,关注阈值变化
错误监控 异常问题发现 快速定位问题,减少故障时间 避免过度上报,注意隐私保护
行为分析 用户体验优化 了解真实使用情况 数据量大,需要合理采样
性能监控 应用性能优化 精确定位性能瓶颈 监控开销要控制在合理范围
监控平台 数据分析展示 统一管理,可视化展示 系统复杂度高,需要专业运维

🎯 实战应用建议

最佳实践

  1. 渐进式部署:从核心指标开始,逐步完善监控体系
  2. 合理采样:避免100%采集,根据业务重要性设置采样率
  3. 告警优化:设置合理的告警阈值,避免告警疲劳
  4. 数据隐私:遵循数据保护法规,只收集必要的信息
  5. 性能影响:监控代码本身要轻量,避免影响应用性能

性能考虑

  • 异步上报:使用requestIdleCallback或Web Worker进行数据上报
  • 批量发送:合并多个监控数据一次性发送,减少网络请求
  • 本地缓存:网络异常时将数据缓存到localStorage
  • 采样策略:对于高频事件使用采样,避免数据量过大

安全性注意事项

  • 数据脱敏:敏感信息要进行脱敏处理
  • 传输加密:使用HTTPS传输监控数据
  • 访问控制:监控后台要有严格的权限控制
  • 数据保留:设置合理的数据保留期限

💡 总结

这5个前端监控体系的关键指标在实际应用中能够全面保障应用质量,掌握它们能让你的应用问题无处遁形:

  1. Core Web Vitals监控:量化用户体验,提供优化方向
  2. JavaScript错误监控:快速发现和定位线上问题
  3. 用户行为分析:深入了解用户使用习惯和痛点
  4. 性能监控:精确定位性能瓶颈,指导优化工作
  5. 监控平台搭建:统一数据管理,实现可视化分析

希望这些监控技巧能帮助你在前端开发中建立完善的质量保障体系,让线上问题早发现、早解决!


🔗 相关资源


💡 今日收获:掌握了5个前端监控体系的关键指标,这些技术在保障应用质量方面非常重要。

如果这篇文章对你有帮助,欢迎点赞、收藏和分享!有任何问题也欢迎在评论区讨论。 🚀

相关推荐
TimelessHaze2 分钟前
🔥 一文掌握 JavaScript 数组方法(2025 全面指南):分类解析 × 业务场景 × 易错点
前端·javascript·trae
jvxiao34 分钟前
搭建个人博客系列--(4) 利用Github Actions自动构建博客
前端
袁煦丞1 小时前
SimpleMindMap私有部署团队脑力风暴:cpolar内网穿透实验室第401个成功挑战
前端·程序员·远程工作
li理1 小时前
鸿蒙 Next 布局开发实战:6 大核心布局组件全解析
前端
EndingCoder1 小时前
React 19 与 Next.js:利用最新 React 功能
前端·javascript·后端·react.js·前端框架·全栈·next.js
li理1 小时前
鸿蒙 Next 布局大师课:从像素级控制到多端适配的实战指南
前端
前端赵哈哈1 小时前
Vite 图片压缩的 4 种有效方法
前端·vue.js·vite
Nicholas681 小时前
flutter滚动视图之ScrollView源码解析(五)
前端
电商API大数据接口开发Cris1 小时前
Go 语言并发采集淘宝商品数据:利用 API 实现高性能抓取
前端·数据挖掘·api
风中凌乱的L1 小时前
vue 一键打包上传
前端·javascript·vue.js