前端优化:懒加载思考 前端页面加载优化
qiyuwang 2024-10-30 05:34 23 浏览 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 事件方面也是有更多详细考虑才行,否则可能会出现监听事件失效的问题。
所以,在应用懒加载的场景,还是要多多考虑,如果页面资源较少,页面整体逻辑较简单,可以不使用懒加载,一次性加载完成体验可能还会更优于懒加载优化效果。
希望大家可以在工作中合理运用懒加载思想,做到页面的更好体验。
相关推荐
- PayPal严重漏洞可通过不安全的JAVA反序列化对象
-
在2015年12月,我在PayPal商业网站(manager.paypal.com)中发现了一个严重的漏洞,这个漏洞的存在,使得我可以通过不安全的JAVA反序列化对象,在PayPal的网站服务器上远程...
- 提醒:Apache Dubbo存在反序列化漏洞
-
背景:近日监测到ApacheDubbo存在反序列化漏洞(CVE-2019-17564),此漏洞可导致远程代码执行。ApacheDubbo是一款应用广泛的高性能轻量级的JavaRPC分布式服务框架...
- 【预警通报】关于WebLogicT3存在反序列化高危漏洞的预警通报
-
近日,我中心技术支撑单位监测到WebLogicT3存在反序列化0day高危漏洞,攻击者可利用T3协议进行反序列化漏洞实现远程代码执行。...
- Apache dubbo 反序列化漏洞(CVE-2023-23638)分析及利用探索
-
在对Apachedubbo的CVE-2023-23638漏洞分析的过程中,通过对师傅们对这个漏洞的学习和整理,再结合了一些新学的技巧运用,从而把这个漏洞的利用向前推了一步。整个过程中的研究思路以及...
- 案例|WebLogic反序列化漏洞攻击分析
-
目前网络攻击种类越来越多,黑客的攻击手段也变得层出不穷,常规的防护手段通常是对特征进行识别,一旦黑客进行绕过等操作,安全设备很难发现及防御。通过科来网络回溯分析系统可以全景还原各类异常网络行为,记录所...
- 【预警通报】关于ApacheOFBizRMI反序列化远程代码 执行高危漏洞的预警通报
-
近日,我中心技术支撑单位监测发现ApacheOFBiz官方发布安全更新,修复了一处远程代码执行漏洞。成功利用该漏洞的攻击者可造成任意代码执行,控制服务器。该漏洞编号:CVE-2021-26295,安...
- 关于OracleWebLogic wls9-async组件存在反序列化远程命令执行高危漏洞的预警通报
-
近日,国家信息安全漏洞共享平台(CNVD)公布了OracleWebLogicwls9-async反序列化远程命令执行漏洞。攻击者利用该漏洞,可在未授权的情况下远程执行命令。该漏洞安全级别为“高危”。现...
- Rust语言从入门到精通系列 - Serde序列化/反序列化模块入门指北
-
Serde是一个用于序列化和反序列化Rust数据结构的库。它支持JSON、BSON、YAML等多种格式,并且可以自定义序列化和反序列化方式。Serde的特点是代码简洁、易于使用、性能高效。...
- Java反序列化漏洞详解(java反序列化漏洞利用)
-
Java反序列化漏洞从爆出到现在快2个月了,已有白帽子实现了jenkins,weblogic,jboss等的代码执行利用工具。本文对于Java反序列化的漏洞简述后,并对于Java反序列化的Poc进行详...
- 关于Oracle WebLogic Server存在反序列化远程代码执行漏洞的安全公告
-
安全公告编号:CNTA-2018-00222018年7月18日,国家信息安全漏洞共享平台(CNVD)收录了OracleWebLogicServer反序列化远程代码执行漏洞(CNVD-2018-13...
- CVE-2020-9484 Apache Tomcat反序列化漏洞浅析
-
本文是i春秋论坛作家「Ybwh」表哥原创的一篇技术文章,浅析CVE-2020-9484ApacheTomcat反序列化漏洞。01漏洞概述这次是因为错误配置和org.apache.catalina....
- 告别脚本小子系列丨JAVA安全(8)——反序列化利用链(下)
-
0x01前言...
- 关于WebLogic反序列化高危漏洞的紧急预警通报
-
近日,WebLogic官方发布WebLogic反序列化漏洞的紧急预警通告,利用该漏洞可造成远程代码执行并直接控制Weblogic服务器,危害极大。该漏洞编号为:CVE-2019-2890,安全级别为“...
- 高危!Fastjson反序列化漏洞风险通告
-
漏洞描述...
- 学习Vulhub的Java RMI Registry 反序列化漏洞
-
这个实验,我们先通过dnslog演示命令执行,然后通过反弹shell获得root权限。JavaRemoteMethodInvocation用于在Java中进行远程调用。RMI存在远程bind的...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- PayPal严重漏洞可通过不安全的JAVA反序列化对象
- 提醒:Apache Dubbo存在反序列化漏洞
- 【预警通报】关于WebLogicT3存在反序列化高危漏洞的预警通报
- Apache dubbo 反序列化漏洞(CVE-2023-23638)分析及利用探索
- 案例|WebLogic反序列化漏洞攻击分析
- 【预警通报】关于ApacheOFBizRMI反序列化远程代码 执行高危漏洞的预警通报
- 关于OracleWebLogic wls9-async组件存在反序列化远程命令执行高危漏洞的预警通报
- Rust语言从入门到精通系列 - Serde序列化/反序列化模块入门指北
- Java反序列化漏洞详解(java反序列化漏洞利用)
- 关于Oracle WebLogic Server存在反序列化远程代码执行漏洞的安全公告
- 标签列表
-
- 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)