• 解决前端性能问题:如何优化大量数据渲染和复杂交互?



    ✨✨祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心!✨✨ 
    🎈🎈作者主页: 喔的嘛呀🎈🎈

    目录

    引言

    一、分页加载数据

    二、虚拟滚动

    三、懒加载

    四、数据缓存

    五、减少重绘和回流

    六、优化图片和资源:

    七、合并压缩文件

    八、使用Web Workers 

    引言

    前端开发中,遇到需要渲染大量数据或者实现复杂交互的情况是很常见的。这些情况可能会导致页面加载缓慢或者交互体验不佳。下面是一些优化策略,可以帮助你解决这类问题:

    一、分页加载数据

    当需要展示大量数据时,将数据分页加载是一种常见的优化方法,可以减少首次加载时的数据量,提高页面加载速度和用户体验。以下是一个简单的示例代码,演示了如何使用JavaScript实现分页加载数据的功能:

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>分页加载数据示例</title>
    7. </head>
    8. <body>
    9. <div id="data-container"></div>
    10. <button id="load-more-btn">加载更多</button>
    11. <script>
    12. const dataContainer = document.getElementById('data-container');
    13. const loadMoreBtn = document.getElementById('load-more-btn');
    14. let currentPage = 1;
    15. const pageSize = 10; // 每页数据量
    16. // 模拟获取数据的函数,返回一个Promise对象
    17. function fetchData(page) {
    18. return new Promise((resolve, reject) => {
    19. // 模拟异步请求数据
    20. setTimeout(() => {
    21. const data = [];
    22. for (let i = 0; i < pageSize; i++) {
    23. data.push(`Item ${(page - 1) * pageSize + i + 1}`);
    24. }
    25. resolve(data);
    26. }, 500); // 模拟延迟
    27. });
    28. }
    29. // 加载更多数据的函数
    30. async function loadMore() {
    31. const data = await fetchData(currentPage);
    32. if (data.length > 0) {
    33. // 渲染数据到页面
    34. data.forEach(item => {
    35. const itemElement = document.createElement('div');
    36. itemElement.textContent = item;
    37. dataContainer.appendChild(itemElement);
    38. });
    39. currentPage++;
    40. } else {
    41. loadMoreBtn.disabled = true; // 没有更多数据可加载时禁用按钮
    42. }
    43. }
    44. // 初始化页面,加载第一页数据
    45. loadMore();
    46. // 点击按钮加载更多数据
    47. loadMoreBtn.addEventListener('click', loadMore);
    48. </script>
    49. </body>
    50. </html>

    在这个示例中,我们使用了一个fetchData函数来模拟异步请求数据的过程,每次请求返回一页数据。然后使用loadMore函数来加载更多数据,并将数据渲染到页面上。点击按钮时,会触发加载更多数据的操作,直到没有更多数据可加载为止。这样就实现了简单的分页加载数据的功能。

    二、虚拟滚动

    虚拟滚动是一种优化技术,用于处理大量数据的列表,在滚动时只渲染可见区域的数据,而不是渲染整个列表,从而提高页面的渲染效率。下面是一个使用JavaScript实现虚拟滚动的示例代码: 

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>虚拟滚动示例</title>
    7. <style>
    8. #container {
    9. height: 300px; /* 可视区域高度 */
    10. overflow-y: scroll; /* 垂直滚动条 */
    11. border: 1px solid #ccc;
    12. }
    13. .item {
    14. height: 50px; /* 每项高度 */
    15. line-height: 50px;
    16. border-bottom: 1px solid #ddd;
    17. text-align: center;
    18. }
    19. </style>
    20. </head>
    21. <body>
    22. <div id="container" onscroll="handleScroll()">
    23. <div id="content" style="height: 5000px;"></div> <!-- 模拟大量数据 -->
    24. </div>
    25. <script>
    26. const container = document.getElementById('container');
    27. const content = document.getElementById('content');
    28. const itemHeight = 50; // 每项高度
    29. let visibleItemCount = Math.ceil(container.clientHeight / itemHeight); // 可见项数量
    30. let start = 0; // 可见区域的起始索引
    31. // 更新可见区域的内容
    32. function updateVisibleItems() {
    33. start = Math.floor(container.scrollTop / itemHeight);
    34. content.style.marginTop = start * itemHeight + 'px'; // 设置内容的marginTop
    35. content.style.marginBottom = (content.scrollHeight - (start + visibleItemCount) * itemHeight) + 'px'; // 设置内容的marginBottom
    36. }
    37. // 滚动事件处理函数
    38. function handleScroll() {
    39. updateVisibleItems();
    40. }
    41. // 初始渲染可见区域的内容
    42. updateVisibleItems();
    43. </script>
    44. </body>
    45. </html>

    在这个示例中,我们通过设置container元素的heightoverflow-y: scroll样式,创建了一个固定高度的可滚动区域。列表的实际内容由content元素表示,其高度为所有项的总高度。然后通过计算可见区域的起始索引start,并设置content元素的marginTopmarginBottom,实现了虚拟滚动的效果。只有可见区域的内容会被实际渲染,从而减少了不可见区域的渲染,提高了页面的渲染效率。

    三、懒加载

    懒加载是一种常见的优化技术,用于延迟加载页面中的图片、视频等资源,只有当它们进入用户的可视区域时才加载,从而减少了页面首次加载时的资源消耗,提高了页面的加载速度。下面是一个使用JavaScript实现图片懒加载的示例代码:

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>懒加载示例</title>
    7. <style>
    8. img {
    9. width: 100%;
    10. height: auto;
    11. }
    12. </style>
    13. </head>
    14. <body>
    15. <div id="container">
    16. <img data-src="image1.jpg" alt="Image 1"> <!-- 设置data-src属性存放真实图片地址 -->
    17. <img data-src="image2.jpg" alt="Image 2">
    18. <img data-src="image3.jpg" alt="Image 3">
    19. <img data-src="image4.jpg" alt="Image 4">
    20. <img data-src="image5.jpg" alt="Image 5">
    21. </div>
    22. <script>
    23. function lazyLoadImages() {
    24. const images = document.querySelectorAll('img');
    25. images.forEach(img => {
    26. const rect = img.getBoundingClientRect();
    27. if (rect.top < window.innerHeight && rect.bottom >= 0 && !img.src) {
    28. img.src = img.getAttribute('data-src'); // 加载真实图片
    29. }
    30. });
    31. }
    32. function handleScroll() {
    33. lazyLoadImages();
    34. }
    35. // 监听滚动事件
    36. window.addEventListener('scroll', handleScroll);
    37. // 初始加载可视区域内的图片
    38. lazyLoadImages();
    39. </script>
    40. </body>
    41. </html>

    在这个示例中,我们将真实的图片地址存放在data-src属性中,而src属性为空。当图片进入用户的可视区域时,通过getBoundingClientRect方法获取图片相对于视口的位置,如果图片的顶部已经进入可视区域且底部未超出可视区域,则将data-src属性的值赋给src属性,从而实现图片的懒加载。这样可以减少首次加载时对图片资源的请求,提高页面加载速度。

    四、数据缓存

    据缓存是一种常见的优化技术,可以减少重复请求,提高数据访问速度。下面是一个简单的示例代码,演示了如何使用JavaScript实现数据缓存的功能:

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>数据缓存示例</title>
    7. </head>
    8. <body>
    9. <button id="fetch-data-btn">获取数据</button>
    10. <div id="data-container"></div>
    11. <script>
    12. const dataContainer = document.getElementById('data-container');
    13. const fetchDataBtn = document.getElementById('fetch-data-btn');
    14. let cachedData = null; // 缓存数据
    15. // 模拟获取数据的函数,返回一个Promise对象
    16. function fetchData() {
    17. return new Promise((resolve, reject) => {
    18. // 模拟异步请求数据
    19. setTimeout(() => {
    20. const data = ['Data 1', 'Data 2', 'Data 3'];
    21. resolve(data);
    22. }, 500); // 模拟延迟
    23. });
    24. }
    25. // 加载数据的函数
    26. async function loadData() {
    27. if (!cachedData) {
    28. cachedData = await fetchData(); // 如果缓存数据为空,则从服务器获取数据
    29. }
    30. renderData(cachedData);
    31. }
    32. // 渲染数据到页面
    33. function renderData(data) {
    34. dataContainer.innerHTML = '';
    35. data.forEach(item => {
    36. const itemElement = document.createElement('div');
    37. itemElement.textContent = item;
    38. dataContainer.appendChild(itemElement);
    39. });
    40. }
    41. // 点击按钮加载数据
    42. fetchDataBtn.addEventListener('click', loadData);
    43. // 初始化页面,首次加载数据
    44. loadData();
    45. </script>
    46. </body>
    47. </html>

    五、减少重绘和回流

    减少重绘和回流是优化页面性能的重要策略之一。重绘(Repaint)是指重新绘制元素的外观而不改变其大小和位置,而回流(Reflow)是指重新计算元素的大小和位置,会导致整个页面布局的重新排列。这些操作都会消耗大量的计算资源,影响页面的性能。以下是一些减少重绘和回流的方法:

    1. 使用transformopacity代替topleft等属性transformopacity不会触发回流,可以用来移动元素或实现淡入淡出效果。

    2. 避免频繁操作样式:尽量一次性修改多个样式,而不是分开多次修改,可以减少重绘和回流的次数。

    3. 使用文档片段(DocumentFragment):在DOM操作时,可以先将要操作的元素添加到文档片段中,然后再一次性将文档片段添加到页面中,减少了多次操作DOM元素导致的重绘和回流。

    4. 批量修改样式:可以使用classList来添加、移除或切换多个类,而不是直接操作元素的className,这样可以减少重绘和回流。

    5. 避免强制同步布局(Forced Synchronous Layouts):在获取某些元素的布局信息(如宽高、位置等)时,会触发回流,可以通过getComputedStyle获取样式而不是直接访问offsetWidthoffsetHeight等属性来避免。

    下面是一个简单的示例代码,演示了如何减少重绘和回流的操作:

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>减少重绘和回流示例</title>
    7. <style>
    8. .box {
    9. width: 100px;
    10. height: 100px;
    11. background-color: red;
    12. transition: transform 0.3s ease; /* 添加过渡效果 */
    13. }
    14. </style>
    15. </head>
    16. <body>
    17. <button onclick="moveBox()">移动盒子</button>
    18. <div class="box"></div>
    19. <script>
    20. const box = document.querySelector('.box');
    21. function moveBox() {
    22. // 避免直接修改topleft属性,改为使用transform
    23. box.style.transform = 'translateX(100px)';
    24. }
    25. </script>
    26. </body>
    27. </html>

    在这个示例中,通过点击按钮移动盒子,使用transform属性而不是直接修改topleft属性,可以减少重绘和回流的次数,提高页面的性能。

    六、优化图片和资源:

    优化图片和其他资源是提高页面加载速度的重要步骤之一。通过压缩图片、使用适当的图片格式以及懒加载等方法,可以减少资源加载时间,提高用户体验。以下是一些优化图片和资源的常用方法:

    1. 压缩图片:使用图片压缩工具(如TinyPNG、ImageOptim等)对图片进行压缩,减小图片文件大小,加快加载速度。

    2. 使用适当的图片格式:根据图片内容选择适当的图片格式,如JPEG、PNG、WebP等。JPEG适用于照片和渐变色图片,PNG适用于图标和透明图片,WebP是一种现代的图像格式,支持有损和无损压缩,通常可以取得更好的压缩效果。

    3. 懒加载图片:对于页面中的图片,可以使用懒加载技术,只有当图片进入用户的可视区域时才加载,减少首次加载时的资源消耗。

    4. 使用CSS Sprites:将多个小图标合并成一个图片文件,并通过CSS的background-position属性来显示不同的图标,减少HTTP请求次数,提高加载速度。

    5. 延迟加载非关键资源:对于一些非关键的资源(如广告、统计代码等),可以延迟加载,等页面主要内容加载完毕再加载这些资源,提高页面加载速度。

    6. 使用CDN加速:使用内容分发网络(CDN)来加速图片和其他静态资源的加载,让用户从离他们更近的服务器获取资源,减少网络延迟。

    下面是一个简单的示例代码,演示了如何使用懒加载技术加载图片:

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <title>懒加载图片示例</title>
    7. <style>
    8. img {
    9. width: 100%;
    10. height: auto;
    11. }
    12. .placeholder {
    13. width: 100%;
    14. height: 200px; /* 设置一个占位高度,避免图片加载时页面布局抖动 */
    15. background-color: #f0f0f0;
    16. }
    17. </style>
    18. </head>
    19. <body>
    20. <div class="container">
    21. <div class="placeholder"></div>
    22. <img data-src="image.jpg" alt="Image">
    23. </div>
    24. <script>
    25. function lazyLoadImages() {
    26. const images = document.querySelectorAll('img[data-src]');
    27. images.forEach(img => {
    28. const rect = img.getBoundingClientRect();
    29. if (rect.top < window.innerHeight && rect.bottom >= 0) {
    30. img.src = img.getAttribute('data-src'); // 加载真实图片
    31. img.removeAttribute('data-src');
    32. }
    33. });
    34. }
    35. function handleScroll() {
    36. lazyLoadImages();
    37. }
    38. // 监听滚动事件
    39. window.addEventListener('scroll', handleScroll);
    40. // 初始加载可视区域内的图片
    41. lazyLoadImages();
    42. </script>
    43. </body>
    44. </html>

    在这个示例中,我们将真实的图片地址存放在data-src属性中,而src属性为空。当图片进入用户的可视区域时,通过getBoundingClientRect方法获取图片相对于视口的位置,如果图片的顶部已经进入可视区域且底部未超出可视区域,则将data-src属性的值赋给src属性,从而实现图片的懒加载。这样可以减少首次加载时对图片资源的请求,提高页面加载速度。

    七、合并压缩文件

    合并和压缩CSS和JavaScript文件是优化页面加载速度的有效方法之一。合并文件可以减少HTTP请求次数,而压缩文件可以减小文件大小,从而提高文件加载速度。以下是一些常用的工具和技术来合并和压缩CSS和JavaScript文件:

    1. 使用构建工具:使用构建工具如Webpack、Gulp或Grunt等,可以方便地将多个CSS和JavaScript文件合并为一个文件,并对文件进行压缩处理。

    2. CSS压缩:可以使用工具如cssnano、CleanCSS等来对CSS文件进行压缩,去除空格、注释和不必要的代码,减小文件大小。

    3. JavaScript压缩:可以使用工具如UglifyJS、Closure Compiler等来对JavaScript文件进行压缩,去除空格、注释和不必要的代码,减小文件大小。

    4. 使用CDN:将合并和压缩后的文件托管到内容分发网络(CDN)上,可以加速文件的加载速度,提高用户访问网站的体验。

    下面是一个简单的示例代码,演示了如何使用Gulp来合并和压缩CSS和JavaScript文件:

    首先,安装Gulp及相关插件:

    npm install gulp gulp-concat gulp-uglify --save-dev
    

    然后,创建一个gulpfile.js文件,配置Gulp任务:

    1. const gulp = require('gulp');
    2. const concat = require('gulp-concat');
    3. const uglify = require('gulp-uglify');
    4. // 合并压缩JavaScript文件
    5. gulp.task('scripts', function() {
    6. return gulp.src('src/js/*.js')
    7. .pipe(concat('all.min.js'))
    8. .pipe(uglify())
    9. .pipe(gulp.dest('dist/js'));
    10. });
    11. // 默认任务
    12. gulp.task('default', gulp.series('scripts'));

    在上面的示例中,我们定义了一个scripts任务,用于合并和压缩src/js目录下的所有JavaScript文件,并将结果保存为all.min.js文件到dist/js目录中。然后通过运行gulp命令来执行该任务。

    需要注意的是,合并和压缩文件可能会影响代码的可读性和调试性,因此在生产环境中使用这些优化方法时,应该保留源文件的备份以便于调试。

    八、使用Web Workers 

    使用 Web Workers 可以在浏览器中运行脚本,这些脚本运行在与主线程分离的线程中。这样可以避免在主线程中执行复杂的计算任务,从而提高页面的响应速度。以下是一个简单的示例代码,演示了如何使用 Web Workers 来执行一个计算任务:

    首先,创建一个名为 worker.js 的文件,内容如下:

    1. // worker.js
    2. self.addEventListener('message', function(e) {
    3. // 接收主线程传递过来的数据
    4. const data = e.data;
    5. // 模拟一个耗时的计算任务
    6. let result = 0;
    7. for (let i = 0; i < data; i++) {
    8. result += i;
    9. }
    10. // 向主线程发送计算结果
    11. self.postMessage(result);
    12. });

    然后,在主线程中,可以这样使用 Web Workers:

    1. // main.js
    2. let resultElement = document.getElementById('result');
    3. let calculateBtn = document.getElementById('calculate');
    4. // 创建一个新的 Web Worker
    5. let worker = new Worker('worker.js');
    6. // 监听 Web Worker 返回的消息
    7. worker.addEventListener('message', function(e) {
    8. resultElement.textContent = e.data;
    9. });
    10. // 监听计算按钮的点击事件
    11. calculateBtn.addEventListener('click', function() {
    12. let number = document.getElementById('number').value;
    13. // 向 Web Worker 发送数据
    14. worker.postMessage(number);
    15. });

     在这个示例中,当用户点击计算按钮时,主线程将用户输入的数据发送给 Web Worker,在 Web Worker 中执行耗时的计算任务,并将结果返回给主线程,最终在页面上显示出来。这样可以避免在主线程中执行耗时的计算任务,提高页面的响应速度。

    通过以上优化策略,可以有效地提高页面的加载速度和交互体验,为用户提供更好的使用体验

  • 相关阅读:
    kvm虚拟机的克隆以及快照
    10.20作业
    MyBatis 如何进行一对一关联查询呢?
    【JUC】中断机制(interrupt,interrupted,isInterrupted)
    【LeetCode-389】找不同
    shutdown的各类参数及用法
    STM32CubeMX环境安装(保姆级)
    MySQL的Json类型个人用法详解
    vue3新一代状态管理器 — pinia的学习与使用
    浅谈C++|STL初识篇
  • 原文地址:https://blog.csdn.net/2201_75809246/article/details/136484997