关于前端性能优化问题,认识网页加载过程和防抖节流

简介: 在下面这篇文章,将讲解关于前端性能优化的一些常见问题。

2.png前端性能优化—网页加载过程、性能优化方法、防抖和节流



这是我参与更文挑战的第13天

平常我们在加载网页的时候,首先需要先加载网页代码,之后渲染出页面,在这个期间会执行若干个 JS 。那么,如果想要让网页呈现速度和渲染速度快,我们就得保证我们的代码在浏览器这个运行环境当中稳定且高效。这就谈到一个前端性能优化的问题。

在下面这篇文章,将讲解关于前端性能优化的一些常见问题。


一、网页加载过程



1、加载资源的形式


网页需要加载的资源,一般包括以下内容:

  • html 代码;
  • 媒体文件,如图片、视频等;
  • javascriptcss


2、加载资源的过程


加载资源的过程需要经过以下几个步骤:

  • DNS 解析:域名 -> IP 地址。
  • 浏览器根据 IP 地址向服务器发起 http 请求。
  • 服务器处理 http 请求,并返回给浏览器。


3、渲染页面的过程


渲染过程 - 1

  • 根据 HTML 代码生成 DOM Tree
  • 根据 CSS 代码生成 CSSOM
  • DOM TreeCSSOM 整合形成 Render Tree

渲染过程 - 2

  • 根据 Render Tree 渲染页面;
  • 遇到 <script> 则暂停渲染,优先加载并执行 JS 代码,完成后再继续执行;
  • 直至把 Render Tree 渲染完成。


4、关于window.onload 和 DOMContentLoaded


window.addEventListener('load', function(){
    //页面的全部资源加载完才会执行,包括图片、视频等
});
document.addEventListener('DOMContentLoaded', function(){
    //DOM 渲染完即可执行,此时图片、视频还可能没加载完 -> 尽量选择此方法
});


二、性能优化



性能优化是一个综合性的问题,永远没有标准答案,下面将从几个方面来讲解性能优化的内容。


1、性能优化原则

  • 多使用内存、缓存或其他方法。
  • 减少 CPU 计算量,减少网络加载耗时。
  • 适用于所有编程的优化方法 —— 用空间来换时间。


2、性能优化的方法

(1) 让加载更快

(2) 让渲染更快


3、让加载更快


(1)减少资源体积

  • 压缩代码: 可以通过压缩代码来减少资源体积,包括 js 文件、 css 文件和图片都可以进行压缩。同时服务器端 可以通过 gzip 算法来对资源进行压缩。


(2)减少访问次数

  • 合并代码: 比如说我们写了三四个文件,但通过打包可能就只剩下一个文件,并且文件里面是以一行的形式出现,或者雪碧图也算是其中一种方式。
  • SSR服务器端渲染:
    服务端把网页和数据一起加载,一起渲染。
    非SSR:先加载网页,再加载数据,再渲染数据,这个过程听起来就优点繁琐。
    早期的 JSPASPPHP,现在的 vue SSR  、React SSR
  • 缓存:
    举个例子:假设有 10 个资源,如果每次请求都要请求 10 次,那这样子是非常耗时的;那如果 10 个资源中有 6 个命中了缓存,则只需要请求另外 4 个。
    那如何做缓存来达到减少访问次数呢?
    前端会在静态资源上加 hash 后缀,根据文件内容计算 hash 。当文件内容不变时,则 hashurl 都不变。此时 url 和文件不变,则会自动触发 http 缓存机制,返回 304


(3)使用更快的网络

  • 通过 CDN 来操作:
    CDN ,即内容分发网络(Content Delivery Network,简称 CDN ),是建立并覆盖在承载网之上,由分布在不同区域的边缘节点服务器群组成的分布式网络。
    CDN 就是一个反向代理,根据地域来做网络服务,主要是前端用来做静态资源加载,通常在前端项目部署的时候进行这项操作。
    我们平常所使用的 bootstrapJQuery 一般都采用 cdn 的形式。
    这里我也不是特别了解 cdn 的内容,只能才疏学浅的进行介绍。


4、让渲染更快


(1)html、css、js和图片层面


  • css 放在 headJS 放在 body 最下面;
  • 尽早开始执行 JS ,用 DOMContentLoaded  触发;
  • 懒加载(图片懒加载,上滑加载更多)。
<img id = "img1" src = "preview.png" data-realsrc = "abe.png"/>
<script type = "text/javascript">
  let img1 = document.getElementById('img1');
    //把真实的图片data-realsrc赋值给预览的图片src
    img1.src = img1.getAttribute('data-realsrc');
    //…… 一系列逻辑操作
</script>


(2)从DOM层面

  • DOM 查询进行缓存;
  • 从频繁进行 DOM 操作,变为合并到一起进行 DOM 结构插入;

注:关于DOM的性能优化我有在之前的一篇文章中讲过,在主题一的第4小点,这里不再进行细讲,大家可以根据自身需求进行查阅~


(3)防抖 debounce 和 节流 throttle

关于防抖和节流的讲解,详细见下方。


三、防抖和节流



1、防抖 debounce


(1)含义: 从频繁执行触发变为只在最后一次触发。

(2)发生场景: 监听一个输入框,如果直接用 keyup 事件,则每输入一个文字都会触发 onchange 事件,频繁执行操作。

(3)防抖解决: 用户输入结束或暂停时,才会触发 change 事件。

(4)引例阐述

假设我们现在要监听一个输入框的值,此时我们直接用 keyup 事件来实现。实现方式如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" id="input1">
    <script type="text/javascript">
         const input1 = document.getElementById('input1');
         input1.addEventListener('keyup', function(){
             console.log(input1.value);
         });
    </script>
</body>
</html>

此时浏览器的显示效果是这样的:

1.png

大家可以发现,当我们使用 keyup 事件处理,每当我们输入一个字母时,浏览器就会频繁进行打印,这样看起来也太耗费性能了。

于是就有了防抖的解决方案,防抖通过对频繁执行的操作变为只在最后一次执行。实现方式如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" id="input1">
    <script type="text/javascript">
         const input1 = document.getElementById('input1');
         let timer = null;
         input1.addEventListener('keyup', function(){
             if(timer){
                 clearTimeout(timer);
             }
             timer = setTimeout(() => {
                 //模拟触发 change 事件
                 console.log(input1.value);
                 //清空定时器
                 timer = null;
             }, 500);
            //  console.log(input1.value);
         });
    </script>
</body>
</html>
复制代码

此时浏览器的显示效果是这样的:

2.png

通过上图发现,当我们在输入框输入字母时,只在最后输入结束时控制台才打印结果出来。而不会像上面那样频繁打印,因此通过防抖,实现了把频繁执行变为了只在最后一次执行的操作。


(5)封装防抖函数

通过上面的例子,相信大家对防抖有了一个基础的了解。接下来我们通过封装函数来实现相同效果的防抖功能。具体代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" id="input1">
    <script type="text/javascript">
        const input1 = document.getElementById('input1');
        // 封装一个防抖函数
        function debounce(fn, delay = 500){
            // timer 是在闭包中的
            let timer = null;
            return function(){
                if(timer){
                    clearTimeout(timer);
                }
                timer = setTimeout(() => {
                    fn.apply(this, arguments);
                }, delay);
            }
        }
        //运用防抖函数实现input输入框触发操作
        input1.addEventListener('keyup', debounce(function () {
            console.log(input1.value);
        }, 600));
    </script>
</body>
</html>


2、节流 throttle


(1)含义: 从频繁执行触发变为每隔一段时间触发一次。

(2)发生场景: 拖拽一个元素时,直接用 drag 事件,则会频繁触发,很容易导致卡顿。

(3)节流解决: 无论拖拽速度多快,都会每隔 100ms 触发一次(这里的 100ms 不是固定值,也可设置成其它的值)。

(4)引例阐述

假设我们现在要拖拽一个元素,并且想要随时拿到该元素被拖拽的值。此时我们直接用 drag 事件来实现。实现方式如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #div1{
            border: 1px solid #ccc;
            width: 200px;
            height: 100px;
        }
    </style>
</head>
<body>
    <div id = "div1" draggable="true">可拖拽</div>
    <script type="text/javascript">
        const div1 = document.getElementById('div1');
        //用节流之前
        div1.addEventListener('drag', function(e) {
            console.log(e.offsetX, e.offsetY);
        })
    </script>
</body>
</html>

此时浏览器的显示效果是这样的:

1.png

大家可以发现,当我们使用 drag 事件处理,每当我们拖拽元素时,浏览器就会频繁进行打印,这样似乎有一点点耗费性能。

于是就有了节流的解决方案,节流通过对频繁执行的操作变为只在每隔一段时间操作。实现方式如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #div1{
            border: 1px solid #ccc;
            width: 200px;
            height: 100px;
        }
    </style>
</head>
<body>
    <div id = "div1" draggable="true">可拖拽</div>
    <script type="text/javascript">
        const div1 = document.getElementById('div1');
        // 用节流之后
        let timer = null;
        div1.addEventListener('drag', function(e){
            if(timer){
            return;
            }
            timer = setTimeout(() => {
                console.log(e.offsetX, e.offsetY);
                timer = null;
            }, 500);
        });
    </script>
</body>
</htm

此时浏览器的显示效果是这样的。

2.png

通过上图发现,当我们在拖拽时,不再像刚刚那样频繁打印,而是有规律的每隔 500ms 打印一次,这样子看起来就比刚刚的频繁触发要好很多了。所以,节流是通过将频繁执行改为每隔一段时间执行。


(5)封装节流函数

通过上面的例子,相信大家对节流有了一个基础的了解。接下来我们通过封装函数来实现相同效果的节流功能。具体代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" id="input1">
    <script type="text/javascript">
        const input1 = document.getElementById('input1');
        // 封装一个节流函数
        function throttle(fn, delay = 500){
            let timer = null;
            return function (){
                if(timer){
                    return;
                }
                timer = setTimeout(() => {
                    fn.apply(this, arguments);
                    timer = null;
                }, delay);
            }
        }
        //运用节流函数实现拖拽时每隔一段时间触发操作
        div1.addEventListener( 'drag', throttle(function(e){
            console.log(e.offsetX, e.offsetY);
        }, 200));
    </script>
</body>
</html>


四、写在最后


关于前端性能优化的一些基础内容就讲到这里啦!如有疑问或文章有误欢迎留言交流~



相关实践学习
Serverless极速搭建Hexo博客
本场景介绍如何使用阿里云函数计算服务命令行工具快速搭建一个Hexo博客。
相关文章
|
19天前
|
前端开发 UED 开发者
颠覆你的前端知识:防抖与节流的区别及实战解析!
【8月更文挑战第23天】在Web前端开发中,处理用户界面交互产生的事件可能会影响性能。为此,我们有两种优化方法:防抖(debounce)和节流(throttle)。防抖确保函数仅在事件停止触发一段时间后执行一次,适用于如搜索自动补全场景。而节流则确保函数按固定时间间隔执行,不管用户操作频率如何。本篇技术博客将深入解析两者差异并提供示例代码,帮助开发者更好地理解和应用这些技巧以提升应用性能和用户体验。
46 0
|
21天前
|
缓存 前端开发 JavaScript
微前端集成优化:让所有子应用体积更小,加载更快!
【8月更文挑战第17天】微前端集成优化:让所有子应用体积更小,加载更快!
32 1
微前端集成优化:让所有子应用体积更小,加载更快!
|
17天前
|
编解码 前端开发 开发者
【前端设计达人必备】揭秘CSS尺寸单位的魔力:从基础到实战,打造灵动响应式网页!
【8月更文挑战第26天】本文深入探讨了CSS中常用的尺寸单位,包括像素(px)、百分比(%)、视窗单位(vw/vh/vmin/vmax)、可伸缩相对单位(em/rem)以及Flexbox和Grid中的fr单位。通过具体案例展示了每种单位的特点及其适用场景。像素适用于固定尺寸元素;百分比和em/rem利于构建响应式布局;视窗单位适合全屏设计;fr单位则能有效管理复杂网格布局的空间分配。掌握这些单位有助于开发者设计出更加灵活、高质量的网页布局。
35 4
|
19天前
|
XML JavaScript 前端开发
哇塞!Web 前端惊现 DOM 元素神操作,一场惊心动魄的网页变革,你准备好了吗?
【8月更文挑战第23天】在Web前端开发中,熟练操作DOM元素至关重要。DOM作为一种编程接口,将HTML/XML文档表示为节点树,便于使用JavaScript访问及修改文档内容与结构。
40 0
|
24天前
|
缓存 编解码 前端开发
优化Web应用性能的前端技巧:从加载时间到用户体验
在现代Web开发中,提升前端性能不仅仅是为了缩短页面加载时间,更是为了提供更流畅的用户体验。本文将探讨几种有效的前端优化技术,包括懒加载、代码拆分、资源压缩以及浏览器缓存策略。通过具体实例和最佳实践,读者将能够掌握如何系统地提高Web应用的响应速度,减少资源消耗,并最终改善用户的整体体验。
|
11天前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
32 0
|
18天前
|
缓存 前端开发 Java
【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(-Xms512m -Xmx1204m)?
【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(-Xms512m -Xmx1204m)?
|
19天前
|
前端开发 数据安全/隐私保护 开发者
热门聚焦!Web 前端 CSS 选择器 —— 解锁精美网页的密码,触动开发者心灵深处!
【8月更文挑战第23天】CSS 选择器是 Web 前端设计中的关键工具,用于精准定位和美化页面元素。主要包括:直观的元素选择器(如 `p`),灵活的类选择器(如 `.my-class`),唯一的 ID 选择器(如 `#unique-div`),以及可根据属性选择的属性选择器(如 `a[title]`)。此外,后代选择器(如 `div p`)、子选择器(如 `ul &gt; li`)和相邻兄弟选择器(如 `h1 + p`)可用于更复杂的选择。通用选择器(如 `*`)则适用于所有元素。通过组合这些选择器,开发者能够创建出既复杂又美观的网页样式,提升用户体验。
26 0
|
19天前
|
前端开发 JavaScript 开发者
揭秘Web前端交互核心:掌握事件对象,让你的网页响应如丝般顺滑!
【8月更文挑战第23天】在Web前端开发中,事件处理是实现用户与网页互动的关键机制。JavaScript通过事件对象(Event Object)让开发者能捕捉并响应用户的动作,如点击或按键等。事件对象封装了事件的相关信息,在事件监听器触发时作为参数传递。本文首先介绍了事件对象的基础概念,接着详细探讨了常用属性和方法,包括`type`、`target`、`currentTarget`及`preventDefault()`等。并通过一个简单的示例展示了如何在事件处理函数中利用事件对象来获取事件类型、触发元素等信息,并演示了如何阻止默认行为。
24 0
|
28天前
|
前端开发
前端网页练习
这篇文章是作者整理的大一时期前端HTML和CSS的实训练习,包括常用标签使用、无序列表和有序列表、表格和表单的HTML代码示例及其运行结果截图。