从场景分析解决页面卡顿问题,性能优化

今天看到一篇不错的文章,记录下来,讲的是如何解决页面卡顿问题,提高页面的性能优化,从3个场景分析,并使用代码分析

场景 1:列表渲染卡顿 ------ 从 "一次性渲染" 到 "虚拟列表"​

使用vue的v-for加载10000+条数据,会明显感到页面的卡顿,

优化效果:
页面加载时间从 5.2 秒降至 0.3 秒​
DOM 节点数量从 12000 + 降至 80+​
滚动时无卡顿,丝滑度提升明显​

bash 复制代码
<div v-for="item in data"></div>
解决办法:虚拟列表​
使用虚拟列表,原理是只加载可视化区域的数据,使用组件 vue-virtual-scroller,优化后的代码如下:

<template>
  <virtual-scroller
    class="order-list"
    :items="allOrders"
    :item-height="60" // 每个列表项的固定高度
    key-field="id"
  >
    <template v-slot="{ item }">
      <div class="order-item">
        {{ item.orderNo }} - {{ item.amount }}
      </div>
    </template>
  </virtual-scroller>
</template>
<script>
import { VirtualScroller } from 'vue-virtual-scroller';
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
 
export default {
  components: { VirtualScroller },
  data() {
    return {
      allOrders: []
    }
  },
  mounted() {
    this.getAllOrders();
  },
  methods: {
    getAllOrders() {
      api.get('/orders/all').then(res => {
        this.allOrders = res.data;
        // 优化点:如果数据量超10万,可配合后端做"滚动加载"
        // (即滚动到底部时请求下一页数据,避免一次性加载过多)
      });
    }
  }
}
</script>
<style scoped>
.order-list {
  height: 600px; /* 固定列表容器高度,确保可视区域可控 */
  overflow-y: auto;
}
.order-item {
  height: 60px;
  line-height: 60px;
  border-bottom: 1px solid #eee;
}
</style>

场景2:接口超时:从串行 到 并行加缓存

一个页面需要4个接口,使用串行方式请求,总耗时 = 接口 1 耗时 + 接口 2 耗时 + 接口 3 耗时 + 接口 4 耗时,会触发接口超时

bash 复制代码
原代码:
// 错误示例:串行请求,耗时叠加
async function loadUserPageData(userId) {
  // 1. 请求用户基本信息(耗时2.5秒)
  const userInfo = await api.get(`/user/${userId}/info`);
  // 2. 请求订单统计(耗时3秒)
  const orderStats = await api.get(`/user/${userId}/order-stats`);
  // 3. 请求收藏列表(耗时2秒)
  const collectList = await api.get(`/user/${userId}/collects`);
  // 4. 请求消息通知(耗时2.8秒)
  const notifications = await api.get(`/user/${userId}/notifications`);
  
  return { userInfo, orderStats, collectList, notifications };
}
// 总耗时:2.5+3+2+2.8=10.3秒(超时)

优化方案:并行请求 + 缓存复用​

1、并行请求:4 个接口无依赖关系(不需要先拿到用户信息再请求订单),用Promise.all同时发起请求,总耗时 = 最长单个接口耗时​

2、缓存复用:用户基本信息、订单统计等数据短时间内不会变化,用localStorage或Vuex缓存,30 分钟内重复进入页面不重复请求​

bash 复制代码
// 优化后:并行请求+缓存
async function loadUserPageData(userId) {
  // 1. 定义缓存key和过期时间(30分钟)
  const CACHE_KEY = `userPageData_${userId}`;
  const CACHE_EXPIRE = 30 * 60 * 1000;
  
  // 2. 先查缓存,未过期则直接返回
  const cacheData = localStorage.getItem(CACHE_KEY);
  if (cacheData) {
    const { data, timestamp } = JSON.parse(cacheData);
    if (Date.now() - timestamp < CACHE_EXPIRE) {
      console.log('使用缓存数据');
      return data;
    }
  }
  
  // 3. 并行发起4个接口请求
  const [userInfoRes, orderStatsRes, collectListRes, notificationsRes] = await Promise.all([
    api.get(`/user/${userId}/info`),
    api.get(`/user/${userId}/order-stats`),
    api.get(`/user/${userId}/collects`),
    api.get(`/user/${userId}/notifications`)
  ]);
  
  // 4. 整理数据并缓存
  const result = {
    userInfo: userInfoRes.data,
    orderStats: orderStatsRes.data,
    collectList: collectListRes.data,
    notifications: notificationsRes.data
  };
  
  localStorage.setItem(CACHE_KEY, JSON.stringify({
    data: result,
    timestamp: Date.now()
  }));
  
  return result;
}
// 总耗时:取最长接口耗时(3秒),且重复进入页面时耗时≈0

场景 3:循环计算耗时 ------ 从 "原生循环" 到 "Web Worker"​

一个数据可视化工具,需要对 10 万条用户行为数据做统计分析(计算留存率、转化率等),原代码在主线程中循环计算,导致页面卡顿 20 秒以上,期间无法点击任何按钮。​

bash 复制代码
原代码:
// 错误示例:主线程中处理大量计算
function calculateUserBehavior(data) {
  const result = {
    retentionRate: 0,
    conversionRate: 0,
    activeUsers: []
  };
  
  // 循环10万条数据做复杂计算(耗时20+秒)
  for (let i = 0; i < data.length; i++) {
    const user = data[i];
    // 1. 计算留存率(判断用户是否连续两天活跃)
    if (user.activeDays >= 2) {
      result.retentionRate += 1;
    }
    // 2. 计算转化率(判断用户是否完成付费)
    if (user.paid) {
      result.conversionRate += 1;
    }
    // 3. 筛选活跃用户(近7天活跃超3次)
    if (user.recentActiveDays >= 3) {
      result.activeUsers.push(user.id);
    }
  }
  
  // 计算最终比率
  result.retentionRate = (result.retentionRate / data.length * 100).toFixed(2) + '%';
  result.conversionRate = (result.conversionRate / data.length * 100).toFixed(2) + '%';
  
  return result;
}
 
// 调用后主线程阻塞
const behaviorData = await api.get('/user/behavior');
const result = calculateUserBehavior(behaviorData.data); // 页面卡顿20秒

优化方案:Web Worker​

核心思路:把复杂计算逻辑转移到子线程(Web Worker)中处理,主线程(UI 线程)保持空闲,用户可以正常操作页面,计算完成后通过事件通知主线程。​

1、页面卡顿消失,计算期间可正常点击、滚动​

2、计算总耗时略有增加(约 22 秒,因线程通信有少量开销),但用户体验大幅提升​

3、若使用SharedWorker,还可实现多标签页共享计算结果(适合多窗口场景)​

bash 复制代码
// 创建 Worker 文件(behavior-calculator.worker.js)
// 子线程:处理计算逻辑,不操作DOM
self.onmessage = function(e) {
  const data = e.data; // 接收主线程传递的数据
  const result = {
    retentionRate: 0,
    conversionRate: 0,
    activeUsers: []
  };
  
  // 10万条数据计算(在子线程中执行,不阻塞主线程)
  for (let i = 0; i < data.length; i++) {
    const user = data[i];
    if (user.activeDays >= 2) result.retentionRate += 1;
    if (user.paid) result.conversionRate += 1;
    if (user.recentActiveDays >= 3) result.activeUsers.push(user.id);
  }
  
  // 计算比率
  result.retentionRate = (result.retentionRate / data.length * 100).toFixed(2) + '%';
  result.conversionRate = (result.conversionRate / data.length * 100).toFixed(2) + '%';
  
  // 向主线程发送计算结果
  self.postMessage(result);
  // 关闭Worker(计算完成后释放资源)
  self.close();
};

async function loadAndCalculateBehavior() {
  // 1. 请求数据
  const behaviorData = await api.get('/user/behavior');
  const rawData = behaviorData.data;
  
  // 2. 创建Worker(注意:本地开发需启动服务,直接打开HTML会跨域)
  const calculatorWorker = new Worker('./behavior-calculator.worker.js');
  
  // 3. 向Worker发送数据
  calculatorWorker.postMessage(rawData);
  
  // 4. 接收Worker返回的结果
  calculatorWorker.onmessage = function(e) {
    const result = e.data;
    console.log('计算完成', result);
    // 更新页面UI(主线程操作,安全)
    renderBehaviorResult(result);
  };
  
  // 5. 处理Worker错误
  calculatorWorker.onerror = function(error) {
    console.error('Worker计算出错', error);
    calculatorWorker.close();
  };
  
  // 优化点:如果用户在计算过程中离开页面,主动关闭Worker
  window.addEventListener('beforeunload', () => {
    calculatorWorker.close();
  });
}
 
// 调用后:页面可正常操作,计算在后台执行
loadAndCalculateBehavior();

性能优化的 3 个核心原则​

定位瓶颈再优化:用 Chrome DevTools(Performance 面板)、Vue DevTools(性能面板)等工具找到卡顿 / 超时的具体原因,不要凭感觉优化(比如盲目用for循环替代forEach,实际性能提升微乎其微)。​

优先解决核心问题:先优化影响用户体验的关键场景(如页面加载、核心功能操作),再处理边缘场景

平衡优化成本与收益:不要为了 0.1 秒的性能提升,写出难以维护的代码(比如过度使用奇技淫巧的语法),可读性和可维护性同样重要。

附上参考文章:https://blog.csdn.net/panjiapengfly/article/details/151142846​

相关推荐
superman超哥3 小时前
Rust Profile-Guided Optimization(PGO):数据驱动的极致性能优化
开发语言·后端·性能优化·rust·数据驱动·pgo
superman超哥3 小时前
Rust 内存对齐与缓存友好设计:性能优化的微观艺术
开发语言·后端·性能优化·rust·内存对齐·缓存优化设计·微观艺术
JQShan17 小时前
同步的 defer,异步的陷阱:Swift 并发中加载动画关不掉的调试实录
性能优化·swift
侑虎科技17 小时前
UE是怎么管理纹理的各向异性采样的
性能优化·gpu
FGGIT1 天前
BoostKit 大数据 OmniRuntime 性能优化原理分析
大数据·性能优化
m0_672656541 天前
JavaScript性能优化实战技术文章大纲
开发语言·javascript·性能优化
LYFlied1 天前
浏览器渲染图层详解
前端·性能优化·图形渲染·浏览器
冬奇Lab1 天前
稳定性性能系列之四——异常日志机制与进程冻结:问题排查的黑匣子
android·性能优化·车载系统·bug
没有bug.的程序员1 天前
Spring Cloud Gateway 架构与执行流程:从原理到性能优化的深度探索
微服务·云原生·eureka·性能优化·架构·sentinel·服务发现