前端优化:懒加载思考 前端页面加载优化
qiyuwang 2024-10-30 05:34 21 浏览 0 评论
杨帆,2018年加入去哪儿,很荣幸能和很多优秀的同事一起打拼,目前在大住宿/大前端/综合业务团队,主要负责专题系统的开发和优化等工作,涉及前端组件化开发和后端 node 工程的开发。希望在大前端架构思想上有所突破,帮助同事和团队在大前端领域里提高开发效率。
一、简介
在进行系统优化的时候,如果只局限于前端范畴的话,所能使用的方法很有限。通常能想到的就是如何让首屏更加流畅,提升用户体验。
懒加载指的是,合理的进行图片、静态资源延迟加载,甚至接口或业务逻辑的延迟执行,做到给首屏最大的性能空间,达到页面首屏更快的渲染效果。
这种思想,适合很多前端工程的优化场景。
二、实现思路
1、资源站位
在懒加载的时候,通常是需要跳过原本的执行步骤。比如:懒加载图片时,需要进行标签的站位,事先设定图片宽高,使用 data-* 属性设置好待请求的图片地址,这时,图片还没有被浏览器下载。
- <img
- class
- =
- "qLazy"
- data-original
- =
- 'https://xyz.abc.com/common/logo.png'
- />
由于没有此时没有设置 src 属性,所以 data-original 地址对应的图片并不会进行加载。
如果直接在 src 属性设置好图片地址,浏览器解析到则会直接下载,那么,就失去了懒加载的意义。
2、监听 scroll 事件
在懒加载时,需要制定懒加载的容器。比如:某些可以滚动的内容列表区域。
但在实际开发中,往往不会显式的制定容器,举个例子:手机端常常会有长列表,随整个页面滚动展现,这时如果进行懒加载优化,默认滚动容器应该为 window 对象。
3、判断资源位置是否在视窗内
这个功能是懒加载功能的核心,因为,懒加载也可以称为延迟加载,但什么时候进行加载呢?一定是用户在看到或者即将要看到待加载资源的时候,提前进行加载。这样,用户在看到资源时,资源已经加载完毕,可以给用户更好体验。
借用网络上一张图片加以说明:
即:当绿色待加载的页面元素,即将进入蓝色可视区域或已经进入可视区域后,执行懒加载方法。
4、判断加载资源或执行相应的回调逻辑
这个功能可以是根据业务进行扩展或更改。懒加载最初的构想只设计在了图片标签上,因为大部分手机端页面 80% 左右都会是图片,能够合理的控制图片的加载就能更好的改善用户体验。但也有例外,随着前端的功能越来越强大,很多渲染工作都会交给前端负责,很多业务逻辑也都又前端进行控制。所以,系统会很希望扩展一些懒执行功能,保证首屏逻辑优先完成,后续渲染或复杂操作延迟执行。
下面是部分懒加载实现源码。
- function
- QLazyLoader
- (
- setting
- )
- {
- var
- _setting
- =
- {
- selector
- :
- '.qLazy'
- ,
- // 事件名
- event
- :
- 'scroll'
- ,
- // 默认绑定 evnet 的对象容器
- container
- :
- setting
- .
- container
- ||
- window
- ,
- // 获取 src 的 data 属性,默认 data-attrbute
- attribute
- :
- 'data-original'
- ,
- // 设置懒加载类型 默认为img 选项: 图片:img 只执行回调:call
- loadtype
- :
- 'img'
- ,
- // 回调事件,元素出现在视口中 function
- appear
- :
- null
- ,
- // 触发 load 事件时执行的回调 function
- load
- :
- null
- };
- //省略:进行滚动监听
- //省略:判断是否出现再视窗内
- //如果元素出现在视窗内
- elements
- .
- each
- (
- function
- ()
- {
- var
- dom
- =
- this
- ;
- var
- jqThis
- =
- $
- (
- dom
- );
- var
- qSrc
- =
- jqThis
- .
- attr
- (
- setting
- .
- attribute
- );
- var
- loadAction
- =
- function
- ()
- {};
- var
- loadedCall
- =
- function
- ()
- {
- elements
- =
- $
- (
- grepElements
- (
- elements
- ));
- if
- (
- setting
- .
- load
- )
- {
- setting
- .
- load
- .
- call
- (
- dom
- ,
- jqThis
- ,
- elements
- .
- length
- ,
- setting
- );
- }
- }
- if
- (
- /img|background-image/
- .
- test
- (
- setting
- .
- loadtype
- )
- &&
- qSrc
- )
- {
- var
- _img
- =
- jqThis
- ;
- // 开始懒加载图片
- if
- (!
- _img
- .
- attr
- (
- 'src'
- ))
- {
- jqThis
- .
- attr
- (
- 'src'
- ,
- qSrc
- );
- }
- _img
- .
- one
- (
- 'error'
- ,
- function
- ()
- {
- console
- .
- log
- (
- 'qSrc loaded error'
- ,
- qSrc
- );
- });
- }
- if
- (
- /js/
- .
- test
- (
- setting
- .
- loadtype
- )
- &&
- qSrc
- )
- {
- loadAction
- =
- function
- ()
- {
- LoadScript
- (
- qSrc
- ,
- loadedCall
- );
- }
- }
- if
- (
- /css/
- .
- test
- (
- setting
- .
- loadtype
- )
- &&
- qSrc
- )
- {
- loadAction
- =
- function
- ()
- {
- LoadStyle
- (
- qSrc
- ,
- loadedCall
- );
- }
- }
- if
- (
- /call/
- .
- test
- (
- setting
- .
- loadtype
- ))
- {
- loadAction
- =
- loadedCall
- ;
- }
- jqThis
- .
- one
- (
- APPEAR_EVENT
- ,
- function
- (
- e
- )
- {
- if
- (!
- dom
- .
- loaded
- )
- {
- if
- (
- setting
- .
- appear
- )
- {
- setting
- .
- appear
- .
- call
- (
- dom
- ,
- $
- (
- this
- ),
- elements
- .
- length
- ,
- setting
- );
- }
- loadAction
- ();
- }
- });
- });
- }
三、如何判断资源位置是否出现在视窗内
1、根据可视区域高度和滚动高度以及元素距离页面顶端的距离进行计算
- function
- isElementInViewport
- (
- el
- )
- {
- // 获取div距离顶部的偏移量
- var
- offsetTop
- =
- el
- .
- offsetTop
- ;
- // 获取屏幕高度
- var
- clientHeight
- =
- document
- .
- documentElement
- .
- clientHeight
- ;
- // 屏幕卷去的高度
- var
- scrollTop
- =
- document
- .
- documentElement
- .
- scrollTop
- ;
- if
- (
- clientHeight
- +
- scrollTop
- >
- offsetTop
- )
- {
- console
- .
- log
- (
- "已经进入可视区"
- );
- }
- else
- {
- console
- .
- log
- (
- "并没有进入可视区"
- );
- }
- }
注意:document.documentElement 的兼容性问题,不在此次讲解范围内。
2、getBoundingClientRect API
这个方法非常有用,常用于确定元素相对于视口的位置。该方法会返回一个 DOMRect 对象,包含 left,top,width,height,bottom,right 六个属性。
left,right,top,bottom: 都是元素(不包括 margin )相对于视口的原点(视口的上边界和左边界)的距离。
- // 用法举例:
- var
- ro
- =
- object
- .
- getBoundingClientRect
- ();
- var
- Top
- =
- ro
- .
- top
- ;
- var
- Bottom
- =
- ro
- .
- bottom
- ;
- var
- Left
- =
- ro
- .
- left
- ;
- var
- Right
- =
- ro
- .
- right
- ;
- var
- Width
- =
- ro
- .
- width
- ||
- Right
- -
- Left
- ;
- var
- Height
- =
- ro
- .
- height
- ||
- Bottom
- -
- Top
- ;
- function
- isElementInViewport
- (
- el
- )
- {
- var
- rect
- =
- el
- .
- getBoundingClientRect
- ();
- return
- (
- rect
- .
- top
- >=
- 0
- &&
- rect
- .
- left
- >=
- 0
- &&
- rect
- .
- bottom
- <=
- (
- document
- .
- documentElement
- .
- clientWidth
- ||
- document
- .
- documentElement
- .
- clientHeight
- )
- &&
- rect
- .
- right
- <=
- (
- document
- .
- documentElement
- .
- clientWidth
- ||
- document
- .
- documentElement
- .
- clientWidth
- )
- );
- }
3、IntersectionObserver API
至于是否使用这个 API ,个人建议了解就好,目前仍处于 w3c 草案阶段 ,并且浏览器实现不太乐观。
这个 API 为开发者提供了一种可以异步监听目标元素与视窗 (viewport) 交叉状态的手段。
下面是一个懒加载模板内容的例子。
- function
- query
- (
- selector
- )
- {
- return
- Array
- .
- from
- (
- document
- .
- querySelectorAll
- (
- selector
- ));
- }
- var
- observer
- =
- new
- IntersectionObserver
- (
- function
- (
- changes
- )
- {
- changes
- .
- forEach
- (
- function
- (
- change
- )
- {
- var
- container
- =
- change
- .
- target
- ;
- var
- content
- =
- container
- .
- querySelector
- (
- 'template'
- ).
- content
- ;
- container
- .
- appendChild
- (
- content
- );
- observer
- .
- unobserve
- (
- container
- );
- });
- }
- );
- query
- (
- '.lazy-loaded'
- ).
- forEach
- (
- function
- (
- item
- )
- {
- observer
- .
- observe
- (
- item
- );
- });
这个 API 使懒加载变得简单了,由于不需要监听容器的滚动事件,全部由原生的观察者进行了异步回调。
四、扩展与优化
1、添加节流
scroll 事件,在滚动过程中,由于检测是否在视窗内的逻辑频繁被触发。因而频繁执行 DOM 操作、资源加载等浏览器负担消耗比较严重的行为。如果不加以控制很可能导致 UI 停顿甚至浏览器崩溃。
- // 添加节流控制。
- if
- (
- _setting
- .
- throttleMS
- >
- 0
- )
- {
- this
- .
- update
- =
- throttle
- (
- this
- .
- update
- ,
- _setting
- .
- throttleMS
- );
- }
2、添加仅垂直方向判断
通常懒加载情况 scroll 事件绑定在 window 对象,整个页面又是从上到下进行渲染,所以,横向的元素判断是否在视窗内通常没有意义。设置开关,可以减少横向判断逻辑,减少资源消耗。
- // 只判断垂直方向元素是否出现在视窗内
- if
- (
- setting
- .
- vertical
- )
- {
- if
- (
- jqThis
- .
- height
- ()
- <=
- 0
- ||
- jqThis
- .
- css
- (
- 'display'
- )
- ===
- 'none'
- ){
- return
- ;
- }
- if
- (
- $
- .
- abovethetop
- (
- this
- ,
- setting
- ))
- {
- // Nothing.
- }
- else
- if
- (!
- $
- .
- belowthefold
- (
- this
- ,
- setting
- ))
- {
- jqThis
- .
- trigger
- (
- APPEAR_EVENT
- );
- counter
- =
- 0
- ;
- }
- else
- {
- if
- (++
- counter
- >
- setting
- .
- failureLimit
- )
- {
- return
- false
- ;
- }
- }
- }
3、扩展功能
前面可能说过,懒加载并不局限与图片情况,有时根据业务需求可以扩展需要延迟加载的静态资源。比如:js 外链脚本、css 外链样式、视频、音频等。或者只放出回调,后续具体业务逻辑交由开发自行处理。
具体思路,可以在初始化时,配置指定参数决定当前懒加载实例执行哪种功能加载。也可以在站位 DOM 中配置属性进行懒加载类型判断,可以根据业务需要灵活处理。
五、总结
懒加载对于前端优化来讲,可以称得上是一种万金油的优化方式,很多场景都可以使用其提升页面体验。
但懒加载核心是判断元素是否在视窗内的操作,这种判断是要在视窗滚动过程中获取元素宽高,导致页面会频繁重绘,会耗费很大的性能开销。
而且在监听 DOM 方面,如果懒加载的逻辑是延迟渲染某段 HTML 代码的话,那么在绑定 DOM 事件方面也是有更多详细考虑才行,否则可能会出现监听事件失效的问题。
所以,在应用懒加载的场景,还是要多多考虑,如果页面资源较少,页面整体逻辑较简单,可以不使用懒加载,一次性加载完成体验可能还会更优于懒加载优化效果。
希望大家可以在工作中合理运用懒加载思想,做到页面的更好体验。
相关推荐
- # 安装打开 ubuntu-22.04.3-LTS 报错 解决方案
-
#安装打开ubuntu-22.04.3-LTS报错解决方案WslRegisterDistributionfailedwitherror:0x800701bcError:0x80070...
- 利用阿里云镜像在ubuntu上安装Docker
-
简介:...
- 如何将Ubuntu Kylin(优麒麟)19.10系统升级到20.04版本
-
UbuntuKylin系统使用一段时间后,有新的版本发布,如何将现有的UbuntuKylin系统升级到最新版本?可以通过下面的方法进行升级。1.先查看相关的UbuntuKylin系统版本情况。使...
- Ubuntu 16.10内部代号确认为Yakkety Yak
-
在正式宣布Ubuntu16.04LTS(XenialXerus)的当天,Canonical创始人MarkShuttleworth还非常开心的在个人微博上宣布Ubuntu下个版本16.10的内...
- 如何在win11的wsl上装ubuntu(怎么在windows上安装ubuntu)
-
在Windows11的WSL(WindowsSubsystemforLinux)上安装Ubuntu非常简单。以下是详细的步骤:---...
- Win11学院:如何在Windows 11上使用WSL安装Ubuntu
-
IT之家2月18日消息,科技媒体pureinfotech昨日(2月17日)发布博文,介绍了3中简便的方法,让你轻松在Windows11系统中,使用WindowsSubs...
- 如何查看Linux的IP地址(如何查看Linux的ip地址)
-
本头条号每天坚持更新原创干货技术文章,欢迎关注本头条号"Linux学习教程",公众号名称“Linux入门学习教程"。...
- 怎么看电脑系统?(怎么看电脑系统配置)
-
要查看电脑的操作系统信息,可以按照以下步骤操作,根据不同的操作系统选择对应的方法:一、Windows系统通过系统属性查看右键点击桌面上的“此电脑”(或“我的电脑”)图标,选择“属性”。在打开的...
- 如何查询 Linux 内核版本?这些命令一定要会!
-
Linux内核是操作系统的核心,负责管理硬件资源、调度进程、处理系统调用等关键任务。不同的内核版本可能支持不同的硬件特性、提供新的功能,或者修复了已知的安全漏洞。以下是查询内核版本的几个常见场景:...
- 深度剖析:Linux下查看系统版本与CPU架构
-
在Linux系统管理、维护以及软件部署的过程中,精准掌握系统版本和CPU架构是极为关键的基础操作。这些信息不仅有助于我们深入了解系统特性、判断软件兼容性,还能为后续的软件安装、性能优化提供重要依据。接...
- 504 错误代码解析与应对策略(504错误咋解决)
-
在互联网的使用过程中,用户偶尔会遭遇各种错误提示,其中504错误代码是较为常见的一种。504错误并非意味着网站被屏蔽,它实际上是指服务器在规定时间内未能从上游服务器获取响应,专业术语称为“Ga...
- 猎聘APP和官网崩了?回应:正对部分职位整改,临时域名可登录
-
10月12日,有网友反映猎聘网无法打开,猎聘APP无法登录。截至10月14日,仍有网友不断向猎聘官方微博下反映该情况,而猎聘官方微博未发布相关情况说明,只是在微博内对反映该情况的用户进行回复,“抱歉,...
- 域名解析的原理是什么?域名解析的流程是怎样的?
-
域名解析是网站正常运行的关键因素,因此网站管理者了解域名解析的原理和流程对于做好域名管理、解决常见解析问题,保障网站的正常运转十分必要。那么域名解析的原理是什么?域名解析的流程是怎样的?接下来,中科三...
- Linux无法解析域名的解决办法(linux 不能解析域名)
-
如果由于误操作,删除了系统原有的dhcp相关设置就无法正常解析域名。 此时,需要手动修改配置文件: /etc/resolv.conf 将域名解析服务器手动添加到配置文件中 该文件是DNS域名解...
- 域名劫持是什么?(域名劫持是什么)
-
域名劫持是互联网攻击的一种方式,通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的地址从而实现用户无法访问目标网站的目的。说的直白些,域名劫持,就是把互...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- # 安装打开 ubuntu-22.04.3-LTS 报错 解决方案
- 利用阿里云镜像在ubuntu上安装Docker
- 如何将Ubuntu Kylin(优麒麟)19.10系统升级到20.04版本
- Ubuntu 16.10内部代号确认为Yakkety Yak
- 如何在win11的wsl上装ubuntu(怎么在windows上安装ubuntu)
- Win11学院:如何在Windows 11上使用WSL安装Ubuntu
- 如何查看Linux的IP地址(如何查看Linux的ip地址)
- 怎么看电脑系统?(怎么看电脑系统配置)
- 如何查询 Linux 内核版本?这些命令一定要会!
- 深度剖析:Linux下查看系统版本与CPU架构
- 标签列表
-
- navicat无法连接mysql服务器 (65)
- 下横线怎么打 (71)
- flash插件怎么安装 (60)
- lol体验服怎么进 (66)
- ae插件怎么安装 (62)
- yum卸载 (75)
- .key文件 (63)
- cad一打开就致命错误是怎么回事 (61)
- rpm文件怎么安装 (66)
- linux取消挂载 (81)
- ie代理配置错误 (61)
- ajax error (67)
- centos7 重启网络 (67)
- centos6下载 (58)
- mysql 外网访问权限 (69)
- centos查看内核版本 (61)
- ps错误16 (66)
- nodejs读取json文件 (64)
- centos7 1810 (59)
- 加载com加载项时运行错误 (67)
- php打乱数组顺序 (68)
- cad安装失败怎么解决 (58)
- 因文件头错误而不能打开怎么解决 (68)
- js判断字符串为空 (62)
- centos查看端口 (64)