性能优化与调试技巧 | 青训营

性能优化与调试技巧

在学习完前端三件套 js、html、css 后,我们已经可以搭建一些基础的页面了。但是大家是否考虑过代码的性能呢,我相信对于大多数初学者(包括我)来说,很少会去考虑性能的问题,毕竟页面都跑不动谁还有时间去考虑性能呢(说多了都是泪)。

但是,当我们已经实现了基础的功能,就需要注重性能的优化了,毕竟前端最重要的就是要优化用户体验,而这时候就涉及到了本次文章的内容,如何优化 JavaScript 代码以提高性能。本次介绍了一些常用的技巧,涵盖了减少重绘和重排、使用节流和防抖技术,以及使用性能分析工具来帮助你优化代码性能。

减少重绘和重排

避免频繁修改样式属性

在短时间内频繁修改元素的样式属性会引发浏览器的重排和重绘。尽量使用 CSS 类来一次性修改多个属性,以减少重排的次数。

js 复制代码
// 避免频繁修改样式属性 
const element = document.getElementById("myElement");
element.style.width = "100px";
element.style.height = "200px";
element.style.backgroundColor = "blue";

可能会在非常短的时间内多次修改 element 的样式属性。如果这样的修改在一个事件处理程序中被重复触发,例如在 resizescroll 事件中,就会导致多次的重排和重绘,从而影响页面性能。

为了避免这种情况,通常推荐将样式的修改集中到一起,以减少重排的次数。例如,你可以使用一个 CSS 类来一次性地修改多个样式属性,然后将这个类应用到元素上:

js 复制代码
const element = document.getElementById("myElement");
element.classList.add("modified-style");

然后在 CSS 中定义这个类的样式:

css 复制代码
.modified-style {
  width: 100px;
  height: 200px;
  background-color: blue;
}

通过这种方式,你可以减少浏览器执行重排和重绘的次数,提高页面的性能。这个优化技巧同样适用于其他需要多次修改样式的情况,例如循环中的 DOM 操作等。

使用 CSS 动画

使用 CSS 过渡和动画而不是 JavaScript 来创建动画效果,因为浏览器可以优化 CSS 动画,避免不必要的重排和重绘。

CSS 过渡和动画通常在一些属性(如位置、尺寸、透明度等)的变化上非常有效,因为浏览器可以根据 CSS 规则进行优化,将动画的渲染过程与浏览器的帧刷新同步。

js 复制代码
const element = document.getElementById("myElement");

// 在某个事件触发时,添加类来触发动画
element.addEventListener("click", () => {
  element.classList.add("animate-slide", "active");
});

当点击元素后,active 类会被添加到元素上,触发 transform 属性的变化。transition 属性指定了过渡效果的时间、缓动函数等信息。由于这种使用 CSS 动画的方式,浏览器会自动处理动画的渲染,从而提高性能和流畅度。

css 复制代码
.animate-slide {
  transition: transform 0.3s ease-in-out; /* 添加过渡效果 */
}

.animate-slide.active {
  transform: translateX(100px); /* 运用过渡效果的属性变化 */
}

总之,使用 CSS 过渡和动画可以充分利用浏览器的优化机制,创建更加流畅的动画效果,同时避免了不必要的 JavaScript 操作和可能引发的性能问题。

批量操作 DOM

JavaScript 中,频繁地操作 DOM 会导致重排,从而影响性能。最好将多个 DOM 操作合并为一个批量操作,或者使用 DocumentFragment 来一次性插入多个元素。

js 复制代码
// 批量操作 DOM 
const container = document.getElementById("container");
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) { 
    const div = document.createElement("div");
    div.textContent = "Item " + i; 
    fragment.appendChild(div); 
} 
container.appendChild(fragment);

通过一次性插入 fragment 到容器中,避免了在循环内频繁修改容器的 DOM ,从而提高了性能。这种技术在需要批量插入大量元素时特别有用,比如在动态生成列表或表格时。通过合并多个操作,可以显著减少浏览器的重排次数,提高页面的响应性。

使用节流和防抖技术

节流(Throttling)

节流是一种限制函数执行频率的技术,确保在一段时间内函数只执行一次。适用于需要限制频繁触发的事件,如滚动、窗口调整等。

js 复制代码
// 节流
function throttle(func, delay) {
  let isThrottled = false;
  
  return function() {
    if (!isThrottled) {
      func.apply(this, arguments);
      isThrottled = true;
      setTimeout(() => {
        isThrottled = false;
      }, delay);
    }
  };
}

这个节流函数的基本思想是,在函数被调用后,将一个标志位 isThrottled 设置为 true,然后使用 setTimeout 在一定时间后将这个标志位重新设置为 false,从而允许下一次函数调用。

我们可以通过将需要进行节流的函数作为参数传递给 throttle 函数,从而创建一个节流后的版本。例如,在滚动事件上使用节流:

js 复制代码
const throttledScroll = throttle(function() {
  console.log("Scroll event throttled");
}, 1000);

window.addEventListener("scroll", throttledScroll);

在这个示例中,throttledScroll 就是经过节流处理后的滚动事件处理函数,它会确保滚动事件在每1000毫秒内只执行一次,避免频繁触发。

防抖(Debouncing)

防抖是一种延迟函数执行的技术,确保在一系列连续触发后只执行一次函数。适用于输入框搜索等需要等待用户输入完成后才执行的情况。

js 复制代码
// 防抖
function debounce(func, delay) {
  let timeout;

  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, arguments);
    }, delay);
  };
}

在每次触发函数时,清除之前的延迟调用(如果有的话),然后使用 setTimeout 来延迟函数的执行。如果在延迟期间再次触发了函数,会重新设置延迟,直到一定时间内没有触发,函数才会执行。

例如,你可以在输入框中使用防抖来实现搜索功能,确保用户输入完毕后再执行搜索:

js 复制代码
const debounceSearch = debounce(function(query) {
  console.log("Searching for:", query);
}, 300);

const inputElement = document.getElementById("searchInput");
inputElement.addEventListener("input", function(event) {
  const query = event.target.value;
  debounceSearch(query);
});

在这个示例中,debounceSearch 是经过防抖处理的搜索函数,它会在用户输入停止后的300毫秒内执行搜索操作,避免了频繁的搜索请求。

代码优化

使用原生方法

原生 JavaScript 方法通常比库和框架提供的方法更高效。例如,使用原生的 DOM 操作方法,或者使用原生的循环语句代替库提供的迭代方法。

js 复制代码
// 使用原生方法
const array = [1, 2, 3, 4, 5];
const sum = array.reduce((total, num) => total + num, 0);

这里使用的是 reduce 方法,它是 JavaScript 数组的一个原生方法,用于对数组中的元素进行累积操作。通过使用原生方法,你可以避免引入外部库,从而减少了代码的复杂性和性能开销。

类似地,你可以使用其他原生方法来操作数组、字符串、对象等,以达到更高效的代码执行。原生方法通常受到浏览器的优化,因此在大多数情况下,它们比库和框架提供的方法更具性能优势。

减少重复代码

重复的代码不仅难以维护,还会增加文件大小。尽量将重复的逻辑抽象为函数或模块。

js 复制代码
// 避免不必要的计算
function calculateTotal(items) {
  let total = 0;

  for (let i = 0; i < items.length; i++) {
    total += items[i].value; // 避免在循环内部重复访问 items[i].value
  }

  return total;
}

在这个示例中,calculateTotal 函数把计算总和的逻辑封装在一个函数中,避免了在循环内部重复计算 items[i].value。通过这种方式,不仅减少了代码的重复,还提高了代码的可读性和维护性。

减少重复代码还包括将相似的代码块抽象为函数、类、模块等,从而可以在多个地方复用这些逻辑,减少错误和改进代码的整体质量。这也有助于保持代码的一致性,减少对代码的维护工作。

懒加载

对于大型页面或图片,使用懒加载技术来延迟加载不可见部分的内容,减少初始加载时间。

js 复制代码
// 懒加载图片
const lazyImages = document.querySelectorAll(".lazy-image");

const lazyLoad = target => {
  const io = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        const src = img.getAttribute("data-src");
        img.setAttribute("src", src);
        observer.disconnect();
      }
    });
  });

  io.observe(target);
};

lazyImages.forEach(lazyLoad);

在这个示例中,每个具有 .lazy-image 类的图片元素都会被添加到 Intersection Observer 中进行观察。当图片进入视口(即可见)时,entry.isIntersectingtrue,然后你可以从 data-src 属性中获取实际图片的 URL,将其设置为 src 属性,从而触发图片加载。

通过这种方式,只有当图片进入可见区域时才会加载,从而减少初始页面加载时间,特别对于需要滚动才能看到的内容非常有效。这有助于提高页面的性能和加载速度。

优化图片和资源

使用适当的图片格式、压缩和缓存,以及使用 CSS Sprite、字体图标等来减少网络请求和提高加载速度。

1. 使用适当的图片格式: 根据图片的内容选择适当的图片格式,如 JPEG、PNG、WebP 等。JPEG 适合照片等内容,PNG 适合透明图像,而 WebP 是一种现代格式,通常可以提供更好的压缩效率和质量。

2. 图片压缩: 使用图片压缩工具来减小图片文件的大小,以减少下载时间。保持图片在合适的质量范围内,避免不必要的高分辨率。

3. 图片缓存: 使用浏览器缓存来存储图片,使用户在再次访问页面时不必重新下载相同的图片资源。通过设置适当的缓存策略,可以减少网络请求,提高加载速度。

html 复制代码
<!-- 在<head>标签中添加以下meta标签 -->
<meta http-equiv="Cache-Control" content="max-age=3600" /> <!-- 缓存资源1小时 -->

4. CSS Sprite: 将多个小图标或图片合并成一张大图,然后通过调整背景位置来显示不同的图标。这可以减少请求次数,提高加载效率。

html 复制代码
<div class="sprite icon-facebook"></div>
<div class="sprite icon-twitter"></div>
css 复制代码
.sprite {
  background-image: url('icons.png');
  background-repeat: no-repeat;
  display: inline-block;
  width: 20px;
  height: 20px;
}

.icon-facebook {
  background-position: 0 0;
}

.icon-twitter {
  background-position: -20px 0;
}

5. 字体图标: 使用字体图标代替图片图标,因为字体图标可以使用 CSS 控制大小和颜色,并且通常比图片图标更小,从而减少下载时间。

html 复制代码
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<i class="fa fa-camera"></i> <!-- 使用 Font Awesome 字体图标 -->

6. 响应式图片: 使用 srcset 属性和 sizes 属性来为不同的屏幕大小提供不同尺寸的图片,以确保在不同设备上显示合适的图像。

html 复制代码
<img srcset="image-small.jpg 320w, image-medium.jpg 640w, image-large.jpg 1024w" sizes="(max-width: 640px) 320px, (max-width: 1024px) 640px, 1024px" alt="An image" />

7. 使用 CDN: 使用内容分发网络(CDN)来提供资源,它可以加速资源的传送,减少用户与服务器之间的延迟。

html 复制代码
<script src="https://cdn.example.com/script.js"></script>
<link rel="stylesheet" href="https://cdn.example.com/styles.css">

以上这些优化方法都有助于提高网站的加载速度,减少用户等待时间,并改善用户体验。在设计和开发过程中,合理使用这些优化方法可以使网站更加高效和可靠。

使用性能分析工具

  • 浏览器开发者工具: 浏览器的开发者工具(如 Chrome DevTools)提供了性能分析功能,可以帮助你分析页面的渲染性能,检测重排和重绘,以及查看 CPU 和内存使用情况。
  • Lighthouse: Lighthouse 是一个开源工具,可以对网页进行自动化的性能和质量审查,帮助你找出优化的机会。

安装 Chrome 扩展程序:

在浏览器使用插件:

查看评分结果:

  • WebPageTest: WebPageTest 允许你在真实浏览器上运行性能测试,提供详细的性能数据和建议。

总结

在我们实现了基本的业务需求后,如果还想进一步地提高性能进而提升用户体验,就需要想办法优化自己代码,让代码变得更加优雅。但优化时也需要细心考虑和测试,以确保在提升性能的同时不引入新问题。借助开发者工具和性能分析工具可以引导我们的优化工作。同时,使用适当的技术和策略,可以减少页面的重绘和重排,适当地应用节流和防抖技术,优化代码逻辑和资源加载。这些都有助于我们创造更出色的用户体验。

相关推荐
夭要7夜宵2 天前
Go 垃圾回收 | 豆包MarsCode AI刷题
青训营笔记
末班车4223 天前
前端框架中的设计模式 | 豆包MarsCode AI刷题
青训营笔记
VanceLLF4 天前
神奇数字组合 | 豆包MarsCode AI刷题
青训营笔记
lann4 天前
Go 程序的优化 | 豆包MarsCode AI刷题
青训营笔记
用户52281271049784 天前
性能优化与调试技巧 | 豆包MarsCode AI刷题
青训营笔记
千慌百风定乾坤6 天前
Go 语言入门指南:基础语法和常用特性解析(下) | 豆包MarsCode AI刷题
青训营笔记
FOFO6 天前
青训营笔记 | HTML语义化的案例分析: 粗略地手绘分析juejin.cn首页 | 豆包MarsCode AI 刷题
青训营笔记
滑滑滑8 天前
后端实践-优化一个已有的 Go 程序提高其性能 | 豆包MarsCode AI刷题
青训营笔记
柠檬柠檬8 天前
Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题
青训营笔记
用户967136399658 天前
计算最小步长丨豆包MarsCodeAI刷题
青训营笔记