You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Apps Script多图表加载慢且偶失败的优化方案咨询

多图表Web应用加载优化方案

针对你遇到的加载慢、偶发图表失败问题,从减少服务器请求、优化数据处理、简化前端逻辑三个核心方向给出优化方案:

一、合并Google Charts初始化,避免重复加载与回调冲突

原代码两次调用google.charts.load,且绑定多个setOnLoadCallback,容易导致加载顺序混乱。改成一次性加载所有需要的图表包,仅绑定一个初始化回调:

document.addEventListener('DOMContentLoaded', function() {
  // 保留原有参数获取逻辑
  const name = document.getElementById("athletename").textContent;
  
  // 一次性加载所有依赖的图表包
  google.charts.load('current', {
    packages: ['corechart', 'line', 'scatter']
  });
  
  // 统一触发数据请求与图表绘制流程
  google.charts.setOnLoadCallback(() => {
    google.script.run
      .withSuccessHandler(drawAllCharts)
      .withFailureHandler((error) => {
        console.error('数据请求失败:', error);
        // 可添加用户可见的错误提示
      })
      .getAllChartsData(name, sDay_load, eDay_load);
  });

  // 通用图表绘制函数,消除冗余代码
  function createAndDrawChart(containerId, dataRows, columns, chartType, options = {}) {
    const data = new google.visualization.DataTable();
    columns.forEach(col => data.addColumn(col.type, col.label));
    data.addRows(dataRows);
    
    let chart;
    switch(chartType) {
      case 'scatter':
        chart = new google.visualization.ScatterChart(document.getElementById(containerId));
        chart.draw(data, google.charts.Scatter.convertOptions(options));
        break;
      default:
        chart = new google.visualization.LineChart(document.getElementById(containerId));
        chart.draw(data, options);
    }
  }

  // 统一处理所有数据并批量绘制图表
  function drawAllCharts(allData) {
    // 体重图表(请根据实际列配置调整columns参数)
    createAndDrawChart('Weight_chart_div', allData.weight, 
      [{type: 'date', label: '日期'}, {type: 'number', label: '体重'}],
      'line', {title: '体重变化'});
    
    // 睡眠图表
    createAndDrawChart('Sleep_chart_div', allData.sleep,
      [{type: 'date', label: '日期'}, {type: 'number', label: '睡眠时长'}],
      'line', {title: '睡眠时长'});
    
    // 心率图表
    createAndDrawChart('HeartRate_chart_div', allData.heartRate,
      [{type: 'date', label: '日期'}, {type: 'number', label: '心率'}],
      'line', {title: '心率变化'});
    
    // 血氧饱和度图表
    createAndDrawChart('OxygenSaturation_chart_div', allData.oxygenSaturation,
      [{type: 'date', label: '日期'}, {type: 'number', label: '血氧饱和度'}],
      'line', {title: '血氧饱和度'});
    
    // 体温图表
    createAndDrawChart('BodyTemperature_chart_div', allData.bodyTemperature,
      [{type: 'date', label: '日期'}, {type: 'number', label: '体温'}],
      'line', {title: '体温变化'});
    
    // 早晨状态图表
    createAndDrawChart('Morning_chart_div', allData.morning,
      [{type: 'date', label: '日期'}, {type: 'number', label: '睡眠质量'}, {type: 'number', label: '疲劳度'}, {type: 'number', label: '食欲'}],
      'line', {title: '早晨状态'});
    
    // 夜晚状态图表
    createAndDrawChart('Night_chart_div', allData.night,
      [{type: 'date', label: '日期'}, {type: 'number', label: '睡眠准备'}, {type: 'number', label: '入睡难度'}],
      'line', {title: '夜晚状态'});
    
    // 早晨症状散点图
    createAndDrawChart('Morning_symptoms', allData.morningSymptoms,
      [{type: 'date', label: '日期'}, {type: 'number', label: '症状指数'}],
      'scatter', {title: '早晨症状'});
    
    // 夜晚症状散点图
    createAndDrawChart('Night_symptoms', allData.nightSymptoms,
      [{type: 'date', label: '日期'}, {type: 'number', label: '症状指数'}],
      'scatter', {title: '夜晚症状'});
  }
});

二、合并服务器端数据请求,优化数据处理逻辑

原代码每个图表单独发起一次服务器请求(共9次),且依赖临时工作表处理数据,耗时极高。改成一次性返回所有图表数据,直接在服务器端完成筛选:

// Google Apps Script 端代码
function getAllChartsData(targetName, startDay, endDay) {
  const wb = SpreadsheetApp.getActiveSpreadsheet();
  const dbSheet = wb.getSheetByName(DBSHEETNAME);
  const allData = dbSheet.getDataRange().getValues();
  const header = allData[0];
  const rows = allData.slice(1);

  // 直接筛选符合日期范围与用户名的数据
  const filteredRows = rows.filter(row => {
    const rowDate = row[0];
    const rowName = row[1];
    return rowDate >= new Date(startDay) && rowDate <= new Date(endDay) && rowName === targetName;
  });

  // 定义各图表需要的列索引(请根据实际表头调整)
  const columnMap = {
    weight: [0, 2], // 日期列、体重列
    sleep: [0, 3], // 日期列、睡眠时长列
    heartRate: [0, 4], // 日期列、心率列
    oxygenSaturation: [0, 5], // 日期列、血氧列
    bodyTemperature: [0, 6], // 日期列、体温列
    morning: [0, 7, 8, 9], // 日期列、睡眠质量、疲劳度、食欲
    night: [0, 10, 11], // 日期列、睡眠准备、入睡难度
    morningSymptoms: [0, 12], // 日期列、早晨症状指数
    nightSymptoms: [0, 13] // 日期列、夜晚症状指数
  };

  // 整理各图表所需数据
  const result = {};
  Object.keys(columnMap).forEach(key => {
    result[key] = filteredRows.map(row => {
      return columnMap[key].map(index => row[index]);
    });
  });

  return result;
}

三、额外优化建议

  • 添加加载状态:在数据请求和绘制过程中显示加载动画,提升用户体验;
  • 图表懒加载:监听滚动事件,仅绘制进入视口的图表,减少初始渲染压力;
  • 数据缓存:使用CacheService缓存同一用户、同一日期范围的请求结果,避免重复计算;
  • 失败重试:给服务器请求添加重试逻辑,处理偶发的请求失败问题。

内容的提问来源于stack exchange,提问作者Cathy Cathy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 00:22:00