前端业务痛点拆解:大文件上传、接口并发… 这些场景该这么破?

179 阅读6分钟

前端业务痛点拆解:大文件上传、接口并发… 这些场景该这么破?

1. ECharts 地图上万级 Icon 展示与瓦片化优化

直接渲染万级 Icon 会导致:

  • 性能瓶颈:大量 DOM 节点或 Canvas 元素渲染阻塞主线程,引发卡顿
  • 视觉混乱:密集 Icon 重叠,信息可读性差

如果有一万个icon需要在echarts地图上展示,可以考虑采用热力图或者聚合图等方式进行展示。这样可以避免大量的marker对页面性能造成影响。同时,如果地图数据过大,可以考虑使用瓦片化技术来进行优化。 瓦片化是一种将地图数据分成小块,以瓦片形式进行加载的技术。通过瓦片化,可以将地图数据分割成多个小块,每个小块都是一个完整的图块,独立进行加载和展示,从而提高页面的性能和响应速度。

解决可以从以下几个方法入手:

1. 分层聚合渲染(推荐)

  • 原理:根据地图缩放层级动态聚合 Icon,层级较低时显示聚合点(如 “100+”),层级足够高时展示具体 Icon

  • 实现步骤

    // ECharts 地图分层配置示例
    option = {
      series: [{
        type: 'map',
        map: 'china',
        zoom: 5, // 初始缩放层级
        label: { show: false },
        roam: true, // 开启缩放交互
        data: generateMassData(10000), // 生成模拟数据(包含经纬度、数值)
        // 自定义聚合规则(根据缩放层级切换渲染策略)
        emphasis: { label: { show: true } },
        itemStyle: {
          normal: { areaColor: '#fff', borderColor: '#111' }
        },
        // 关键:使用自定义渲染器处理海量数据
        progressive: 400, // 渐进式渲染,每次处理 400 个数据点
        progressiveThreshold: 3000 // 当数据量超过 3000 时启用渐进式渲染
      }]
    };
    
    // 聚合函数:根据当前缩放层级分组
    function aggregateData(data, zoom) {
      if (zoom < 6) return [{ name: '全国', value: data.length }]; // 层级低时显示总数
      if (zoom < 8) return data.groupBy('province'); // 层级中等时按省份聚合
      return data; // 层级高时显示全部
    }
    

2. 瓦片化(Tile-Based)方案

  • 核心思想:将地图划分为固定大小的瓦片(如 256x256 像素),每个瓦片独立加载和渲染

  • 技术实现: 使用 Canvas 或 WebGL 渲染瓦片,通过视口滚动加载当前可见区域的瓦片

  • 优势:1. 按需加载,减少内存占用 2. 支持离线缓存,提升二次访问速度

  • 适用场景: 超大规模地图(如全球数据)、需要频繁缩放的场景

3. 替代方案:热力图 / 密度图

// 热力图配置示例
option = {
  series: [{
    type: 'heatmap',
    coordinateSystem: 'geo',
    data: data.map(item => [item.lng, item.lat, item.value]),
    pointSize: 8, // 热点半径
    blurSize: 15, // 高斯模糊范围
    emphasis: { itemStyle: { opacity: 0.8 } }
  }]
};

2. 1000w 行表格如何渲染?

直接渲染千万行数据会导致:

  • 内存爆炸:DOM 节点数量突破浏览器极限
  • 交互卡顿:滚动、筛选等操作触发重排 / 重绘耗时严重
eg:维格表、飞书表格、钉钉表格、在线表格等追求大数据量
可以从以下几个方面考虑:
1. dom
2. 虚拟表格
3. canvas table
4.可视区绘制算法优化
5.canvas 结合 Webassembly 技术实现 (Skia + Webassembly)

3. js 超过 Number 最大值的数怎么处理?

  1. 使用 bigint 来处理大数据
// 基本用法
const bigNumber = 123456789012345678901234567890n; // 末尾加 `n` 标识 BigInt
const result = bigNumber + 987654321098765432109876543210n; // 直接运算

// 注意事项:
// - 不能与普通 Number 混合运算
// - 无浮点型,需手动处理小数位(如以分为单位存储整数)
  1. Decimal.js 库来处理
// 金融场景:精确计算金额
import Decimal from 'decimal.js';

const price = new Decimal('199.99');
const quantity = new Decimal('10');
const total = price.mul(quantity).toFixed(2); // 1999.90(避免浮点精度问题)
  1. 使用 big.js 库 格式化成用户好读的格式
// 科学计算:处理超大指数
import Big from 'big.js';

const avogadro = new Big('6.02214076e23');
const result = avogadro.times('1e20').toString(); // 6.02214076e43

4. 如何解决页面请求接口大规模并发问题?

处理:滑动窗口,算法,专门来控制流量

背景:数据采集平台,低代码编辑平台,有序相对稳定发送到后端

方案:

  1. 请求队列: queue.push() queue.shift() 进行封装
  2. 防抖/节流:

防抖—确保在指定时间内函数只执行一次,常用语输入框搜索建议

节流—确保在指定时间间隔内函数执行一次,常用语窗口的 resize,scroll 事件。

【简单描述:在不调用库的情况下,如何实现,如何封装】

  1. 分页加载

落地及反思:

  1. 大数据量请求场景下,我们选用了请求队列,我主导封装了请求队列
  2. 防抖截流,用户交互层民商解决减少请求的处理
  3. 分页/滚动加载,可视区绘制

5. 请说说大文件上传?

背景:eg Ai 产品方面,设计到用户自定义模型(1G 以上)

存在问题:

  • 网络断开之后,之前传的没了
  • 传着传着 网络波动了,结果啥都没了
  • 关机了,想接着传,做不到

专业术语:

  • 断点续传
  • 断开重连重传
  • 切片上传

方案:

-前端切片 chunk 1024M(1048576K),500K, const size = 1048576/500

-将切片传递给后端,切的片要取名:hash,index

-后端组合切片

前端切片主进程做卡顿, web-worker 多线程切片,处理完后交给主线程发送。切完后,将 blob 存储到 IndexedDB,下次用户进来之后,嗅探一下是否存在未完成上传的切片,有就尝试继续上传。引入 websocket 实时通知,和请求序列的控制 wss。

实现:

6. 前端怎么实现页面截图?

背景:

飞书文档,内容在列表页想要查看【此处参考问卷调查的查看】

内容导出为 png

设计类软件,出图

以内容导出为 png 为例:eg 导出医疗单子/文档导出 png

截图方案:

-canvas

-puppeteer(无头 headless 浏览器),无头表格,无头,UI

-html2canvas(canvas)可以降低截图实现的成本

上传 CDN 确保访问 URL 也能获取截图

落地:

考虑三个场景页面截图,全页面截图/局部截图/特定区域截图

-截图工具的时候要考虑通用性,selectereg:body,hedaer 等,通过 dom 选择器去考虑截图范围

-设计具体协议

-函数式/组件式去实现

-隐藏 canvas

7. H5 移动端适配问题如何解决?

背景:项目想支持 PC,移动端

方案:

-根据端来开发不同页面(成本最高)

-根据不同端加载不同的 CSS 样式(可取)

-根据响应式,运行不同的样式规则(常用)

-style 预处理器

需考虑的问题:

### 设置视窗,通过元信息配置 meta

‹meta name="viewport" content="width=device-width, initial-scale=1.0">

###掌握媒体查询
body {
 font-size: 16px;
}

/* 在某一些设备尺寸下,size 按需更改*/
/* 当我的尺寸 大于 xxx 小于 xxx 的时候,需要什么样式?*/
@media (min-width: 780px) and (max-width: 1024px) {
 body {
   font-size: 18px;
 }
}