面对大数据量渲染,前端工程师如何保证页面流畅性?

一、问题背景

在web前端开发中,需要渲染大量数据是很常见的需求。拿一般的业务系统来说,一个模块中往往需要显示成百上千条记录,这已经属于比较大的数据量。而一些大型系统,如数据分析平台、监控系统等,需要同时渲染的 数据量可能达到几十万甚至上百万。

面对大数据量渲染的需求,前端开发工程师就面临巨大的性能优化压力。如果直接渲染全部数据,页面会出现明显的卡顿甚至假死,严重影响用户体验。那么如何在实现功能的同时,保证页面的流畅和响应呢?这需要我们采取各种优化方案。

本文将围绕“给你10万条数据,请顺滑地渲染出来”这一需求展开,讨论前端性能优化的方方面面,以供同行参考。

二、网页性能优化

在具体分析优化大数据量渲染方案之前,我们先来看看影响网页性能的一般因素有哪些。优化网页性能可以从多方面进行,统筹多种优化手段才能取得事半功倍的效果。

1. 减少HTTP请求数量

页面包含的资源数量越多,尤其是较大的资源,加载时间越长。减少请求数量可以有效优化页面性能。具体可以从以下几点着手:

(1)合理设置页面缓存,对不经常变动的资源启用缓存,避免每次加载都从服务器请求。

(2)资源按需加载,只在需要时加载。

(3)代码按功能拆分,避免把所有代码打包在一起。

(4)图片懒加载,按需异步加载。

(5)合理使用图片 Sprite。

(6)使用 HTTP/2,支持多路复用,更有效地使用连接。

2. 压缩资源体积

压缩 JavaScript、CSS、图片等资源文件,减少文件体积,能明显减少加载时间。常用的压缩方法有:

(1)移除代码中的注释、调试信息。

(2)缩小变量名长度。

(3)删除不必要的空格、缩进。

(4)图片压缩,转换格式等。

3. 使用 CDN 加速资源加载

CDN 网络覆盖广、访问速度快,使用 CDN 加载静态资源能显著提升加载速度。

4. 启用 GZIP

启用 GZIP 压缩,能够减小文件体积,减轻服务器负载。

5. 缓存静态资源

对 CSS、JS、图片等静态资源开启缓存,避免每次请求都访问服务器,可大幅提升重复加载速度。

6. 使用 SSD 硬盘

使用 SSD 固态硬盘替代传统硬盘,能显著提升服务器响应速度。

7. 使用缓存代理

使用如 Nginx、Varnish 等缓存代理,利用其缓存能力减少请求到达应用服务器。

8. 数据库优化

合理设计数据库表结构,添加索引,优化查询语句,提升数据读取速度。

9. 程序优化

优化后端应用程序源码,减少不必要的计算和 I/O 操作。

综上所述,优化网页性能需要从网络资源、服务器端、应用程序等全方位进行优化,才能取得明显效果。接下来我们看看针对大数据量渲染的前端优化手段。

三、前端大数据量渲染优化

当需要前端渲染上万条以上数据时,常见的优化手段包括:

1. 合理分页

不要直接渲染全部数据,可以每次只渲染数据的一个分页,比如每页 100 条记录。当用户滚动到分页底部时,再异步加载下一页数据。

2. 虚拟列表

虚拟列表技术只渲染用户当前可见区域的数据,不渲染整个列表,复用 DOM 元素,避免大量 DOM 操作。例如 Vue 中的 vue-virtual-scroller 组件。

3. 防抖处理

对滚动、输入等高频事件进行防抖处理,避免触发过多无意义的计算。

4. 缓存复用

使用 keep-alive 缓存组件,避免重复渲染,提升切换效率。

5. 数据变更优化

对于需频繁更新的数据,避免直接操作 DOM,使用诸如 virtual DOM、dirty checking 等机制进行优化。

6. 图片懒加载

图片只在将要进入可视区域时才加载,减少不必要的提前加载。

7. 动画优化

优化过渡动画,避免动画过程中产生大量中间帧,导致卡顿。

8. 监控性能

在开发和生产环境监控页面性能,分析找到优化方向。

9. 代码拆分

避免把所有代码打包在一起,按需异步导入,减少初始包体积。

10. SSR 渲染

使用服务端渲染,使得初始状态更快呈现,优化载入速度。

11. Web Worker

利用 Web Worker 进行计算任务,避免主线程被占用,影响页面响应。

12. Tree Shaking

启用 Tree Shaking,移除没有使用的代码,减少打包后的代码量。

四、大数据量渲染优化方案

根据以上的分析,下面我们可以给出一些具体的大数据量渲染优化方案:

1. 动态渲染可视区域

这个优化点也是虚拟列表的核心 —— 只渲染用户当前可见的区域,不渲染整个巨大列表。

具体来说,我们可以在列表容器添加滚动监听,当滚动结束后,计算出当前可见区域的起始和结束索引,然后只渲染这个截断后的范围,复用之前的 DOM 元素。

这样就避免了大量 DOM 操作和不必要的渲染工作,优化性能。

2. 分页加载数据

并不需要将全部数据一次性加载完成,可以按分页逐步加载。一开始只加载第一页数据,当用户滚动到底部时,再加载下一页数据。

前端向服务器请求下一页数据时,传递下一页的页码,服务器根据页码返回对应数据。

分页加载可以分担服务器一次性承受巨大请求的压力,也减少了前端渲染全部数据的性能损耗。

3. 启用路由缓存

使用 vue-router 的路由缓存功能,对组件进行缓存,避免每次路由切换都重新渲染:

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // 始终滚动到顶部
    return { x: 0, y: 0 } 
  }
})

然后在router-view处使用 keep-alive 包裹:

<keep-alive>
  <router-view />
keep-alive>

这样就可以利用缓存提高页面切换效率。

4. 图片懒加载

使用图片懒加载,只有当图片进入可视区域时才加载。常用的实现方式是先使用一张低质量占位图,当滚动到可视区域时再加载真实图片。

这样可以减少页面初始化时加载大量图片产生的带宽和计算pressure。

5. 监控长列表滚动

监听长列表的滚动事件,分析触发频率:

let lastScrollTime = 0 
list.addEventListener('scroll', event => {
  const now = Date.now()
  // 使用防抖,100ms内只记录一次
  if (now - lastScrollTime > 100) {
    lastScrollTime = now
    // 对滚动事件进行采样分析
  }
})

如果监测到一定时间内滚动事件过于频繁,可能表示出现了界面卡顿,可以及时定位问题所在。

6. 虚拟化长列表

结合前面的动态渲染可视区域和分页加载概念,我们可以进一步实现一个虚拟列表组件:

// VirtualList.js

data() {
  return {
    rawList: [], // 原始数据 
    visibleData: [] // 当前渲染的列表项数据
  }
},

// 当滚动时计算 visibleData
calculateVisibleData() {
  // ..
},

render() {
  return (
    <div ref="container" onScroll={this.handleScroll}>
      {this.visibleData.map(item => (
        <Item>{item}</Item>
      ))}
    </div>
  )
}

使用该组件时,只需要渲染 container,滚动时组件内部实现优化,大大减少 DOM 操作。

7. 图表虚拟化

对于含大量图表的页面,我们也可以只渲染当前可视区域的图表,其他图表用一个 div 占位。当滚动到某个占位图表时,再实际渲染该图表。

图表组件可以这样实现:

data() {
  return {
    placeholder: <div className="placeholder" />
  }
},

checkInView() {
  if (inViewport(this)) { 
    // 触发真实渲染
  } else {
    return this.placeholder;
  }
}

render() {
  return this.checkInView();
}  

这样只渲染当前可见的图表,避免大量图表绘制工作占用资源。

8. 对复杂图表进行虚拟化

对于特别复杂的图表,我们甚至可以只渲染简化的低像素占位预览图,真正渲染高清图表只有当图表可见时才进行。

const preview = renderLowResChart(); // 绘制简化预览图

document.addEventListener('scroll', () => {
  const chart = findVisibleChart();
  if (chart) {
    // 渲染高清图表
  }
}); 

这种策略可以极大减少初始化图表绘制时间。

9. 分块渲染大表格

对于超大数据量的表格,我们也可以进行分块渲染:

let renderedRows = 0;

function renderMoreRows() {
  // 每次渲染 20 条
  for (let i = renderedRows; i < renderedRows + 20; i++) { 
    drawRow(data[i]) 
  }
  
  renderedRows += 20;
}

document.addEventListener('scroll', () => {
  if (nearBottom()) {
    renderMoreRows();
  } 
})

按需分块渲染可大幅提升超大表格的流畅性。

10. 复杂页面分区渲染

大型页面可以把复杂组件按区域划分,每个区域渲染可以独立进行,互不影响。

比如 conversataional UI 中,可以把对话内容和使用信息分别包裹在不同容器内:

<div class="chat-area">
  
div>

<div class="info-area">
  
div>

两个区域互不影响,拆分成两个较小的渲染作用域。

类似的,我们可以把页面分成头部、内容区、侧边栏等区块进行渲染,提升整体效率。

五、性能监控

优化是持续的工作,我们需要监控应用在生产环境中的实际性能表现,以便获知优化效果和发现问题。

1. 监控 FPS

FPS(每秒帧数)是衡量界面的流畅程度的主要指标。FPS 过低通常会让用户感觉到界面卡顿。

我们可以通过 requestAnimationFrame 进行 FPS 监控:

let fps = 0;
let lastUpdate = Date.now();

function tick() {
  // 每秒更新一次 FPS
  if (Date.now() - lastUpdate >= 1000) {
    console.log(fps); // 打印出每秒 FPS
    
    fps = 0; 
    lastUpdate = Date.now();
  }

  fps++;
  requestAnimationFrame(tick);
}

观察 FPS 的变化情况,当出现大幅波动时可以探查原因。

2. 监控主线程空闲时间

主线程空闲时间可以看出主线程是否过于繁忙,导致响应卡顿。

使用 Performance API 可以获取该指标:

// 每秒采样一次
setInterval(() => {
  const idleTime = performance.getIdleTime(); 
  console.log(idleTime);
}, 1000);

如果空闲时间持续过低,表示存在优化空间。

3. 分析长任务

长任务也会占用主线程时间片,导致干扰响应。

可以通过监听长任务事件进行分析:

const observer = new PerformanceObserver(list => {
  list.getEntries().forEach(entry => {
    // entry.attribution 包含了长任务的信息 
    console.log(entry.attribution); 
  })
});

observer.observe({ entryTypes: ['longtask'] }); 

常见的长任务包括复杂计算、大数据量渲染等。

4. 模拟慢网络环境

使用模拟工具模拟 2G、3G 等慢网络环境,观察应用性能表现是否存在问题。

5. 进行负载测试

使用工具模拟不同的负载情况,观察应用的承载能力上限。

六、结语

本文围绕大数据量渲染的性能优化进行了比较全面的讨论。实现高效渲染需要从页面整体出发,进行深入理解和细致优化。主要手段包括:

  • 代码层面:虚拟列表、懒加载、防抖处理等
  • 异步分块:分页、分区渲染、图表分块等
  • 资源层面:缓存、CDN、压缩等
  • 工程化:监控、负载测试等

实际开发中,需要根据具体场景进行技术选型和权衡利弊。本文提供的思路和方法供同行参考借鉴,也欢迎与大家进行更深入探讨与交流,让我们共同进步!

你可能感兴趣的:(文章分享,前端,前端优化,前端面对大数据,大数据渲染,性能优化)