按需加载是前端性能优化中的一项重要措施,按需加载是如何定义的呢?顾名思义,指的是当用户触发了动作时才加载对应的功能。触发的动作,是要看具体的业务场景而言,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更改等。加载的文件,可以是JS、图片、CSS、HTML等。后面将会详细介绍“按需”的理解。

按需解析HTML

按需解析HTML,就是页面一开始不解析HTML,根据需要来解析HTML。解析HTML都是需要一定时间,特别是HTML中包含有img标签、引用了背景图片时,如果一开始就解析,那么势必会增加请求数。常见的有对话框、下拉菜单、多标签的内容展示等,这些一开始是不需要解析,可以按需解析。实现按需解析,首先用script 这个标签来忽略对HTML的解析。然后根据触发的动作,script里面的HTML获取出来,填充到对应的节点中

示例代码如下:

<script type="text/x-template" id="suc_subscription">
<!--假设这里的样式box-dytz 引用了一张背景图--->
<div class="box-dytz">
<!--这里暂且用这张图片作为测试,实际中,大家可以替换为任何图片-->
<img src="http://tid.tenpay.com/wp-content/uploads/2012/12/按需加载.jpg" />
</div>
</script>
<div id="success_dilog"></div>
<input type="button" value="点我展示HTML" onclick="showHTML()" />
<script>
function showHTML(){
document.getElementById('success_dilog').innerHTML = document.getElementById('suc_subscription').innerHTML;
}
</script>

我们一起来看下demo,当运行demo并抓包发现:当页面加载结束时,并没有看到图片的请求;当点“点我展示HTML”按钮时,通过抓包发现有图片请求。

曾经做个demo并经过测试发现,如果是直接解析HTML(不包含有请求CSS图片和img标签),耗费的时间要比用<script type=”text/x-template” >缓存html大约慢1-2倍,如果是还包括请求有CSS图片、img标签,请求连接数将会更多,可见按需解析HTML,对性能提升还是有一定效果。

按需加载图片

按需加载图片,就是让图片默认开始不加载,而且在接近可视区域范围时,再进行加载。也称之为懒惰加载。大家都知道,图片一下子全部都加载,请求的次数将会增加,势必影响性能。

先来看下懒惰加载的实现原理。它的触发动作是:当滚动条拉动到某个位置时,即将进入可视范围的图片需要加载。实现的过程分为下面几个步骤:

  • 生成<img data-src=”url”>标签时,用data-src来保存图片地址;
  • 记录的图片data-src都保存到数组里;
  • 对滚动条进行事件绑定,假设绑定的函数为function lazyload(){};
  • 在函数lazyload中,按照下面思路实现:计算图片的Y坐标,并计算可视区域的高度height,当Y小于等于(height+ scrollTop)时,图片的src的值用data-src的来替换,从而来实现图片的按需加载;

下面看一个示例代码:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style type="text/css" rel="stylesheet">
li{float:left; width:200px;}
.veryhigh{height:1000px; border:1px solid pink;}
.ul{width:600px;}
.ul img{width:150px; height:150px;}
</style>
</head>
<body>
<div class="veryhigh"> 空白div 为了出现滚动条</div>
<ul class="ul">
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/dengni8.jpg" /></li>
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/chunseliaoren3.jpg" /></li>
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/dengni8.jpg" /></li>
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/chunseliaoren3.jpg" /></li>
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/dengni8.jpg" /></li>
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/chunseliaoren3.jpg" /></li>
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/dengni8.jpg" /></li>
<li><img data-src="http://img0.bdstatic.com/img/image/shouye/chunseliaoren3.jpg" /></li>
</ul>
<script type="text/javascript">
var API = {
on: function(element, type, handler){
return element.addEventListener ? element.addEventListener(type, handler, false) : element.attachEvent('on'+type, handler);
},
bind: function(obj, handler){
return function(){ handler.apply(obj, arguments); }
},
pageX: function(ele){
var left = 0;
do{
left += ele.offsetLeft;
}while( ele.offsetParent && (ele = ele.offsetParent).nodeName.toUpperCase() !=='BODY')
return left;
},
pageY: function(ele){
var top = 0;
do{
top += ele.offsetTop;
}while( ele.offsetParent && (ele = ele.offsetParent).nodeName.toUpperCase() !=='BODY')
return top;
},
hasClass: function(ele, cls){
return new RegExp('\\b' + cls + '\\b').test(ele.className);
},
addClass: function(ele, cls){
ele.className = ele.className === '' ? cls : this.hasClass(ele, cls) ? '' : ' '+cls
},
attr: function(ele, attr, val){
if(arguments.length === 2){
return ele.attributes[attr] ? ele.attributes[attr].nodeValue : undefined;
}else if(arguments.length === 3){
ele.setAttribute(attr, val);
}
}
}; // API end
// 按需加载图片
function LazyLoad(obj){ //obj:图片区域元素的id
this.lazy = typeof obj == 'string' ? document.getElementById(obj) : document.body;
this.aImg = this.lazy.getElementsByTagName('img');
this.fnLoad = API.bind(this, this.load);
this.load();
API.on(window, 'scroll', this.fnLoad);
API.on(window, 'resize', this.fnLoad);
}
LazyLoad.prototype = {
load: function(){
var iScrollTop = document.documentElement.scrollTop ||document.body.scrollTop;
// 滚动高度+视口高度
var iClientHeight = document.documentElement.clientHeight + iScrollTop;
var i = 0,
aParent = [],
oParent = null,
iTop = 0,
iBottom = 0,
aNotLoaded = this.loaded(0),
notLoadedLen = aNotLoaded.length;
if(this.loaded(1).length !== this.aImg.length){//已加载图片数 不等于 总图片数
for(i = 0; i < notLoadedLen; i++){
var iTop = API.pageY(aNotLoaded[i]) - 200,
iBottom = API.pageY(aNotLoaded[i]) + 200; //未加载图片 前后200px的位置 var isTopArea = iTop>iScrollTop && iTop<iClientHeight ? true : false;
var isBottomArea = iBottom>iScrollTop && iBottom<iClientHeight ? true : false; if(isTopArea || isBottomArea){//前200px位置 位于视窗内,或后200px位于视窗内
aNotLoaded[i].src = API.attr(aNotLoaded[i], 'data-src') || aNotLoaded.src;
API.addClass(aNotLoaded[i], 'loaded');
}
}
}
},
//已加载和未加载图片的数组
loaded: function(status){
var array = [];
for(var i = 0; i<this.aImg.length; i++){
var hasClass = API.hasClass(this.aImg[i], 'loaded');
if(!status && !hasClass){ array.push(this.aImg[i]); }
if(status && hasClass){ array.push(this.aImg[i]); }
}
return array;
}
}; //LazyLoad.prototype end //页面加载完成
API.on(window, 'load', function(){ new LazyLoad(); });
</script>
</body>
</html>

运行上述的示例代码,并抓包会发现:一开始并没有看到图片的请求,但当拉动滚动条到页面下面时,将会看到图片发送请求。目前很多框架都已经支持图片的懒惰加载,平时在开发中,大家可以对图片实现懒惰加载,这是有效提升性能的一个方法,特别是网页图片比较多时,更加应该使用该方法

按需加载除了上述场景外,还有更多的场景。如下图:

页面一开始,加载的是“全部”标签里面的内容,但在点击“指定商品折扣券”标签时,才去加载对应的图片。实现思路如下:

  • 生成<img data-src=”url”>标签时,用data-src来保存图片地址;
  • 在点击标签事件时,获取所有图片,图片的src的值用data-src的来替换;

示例代码如下:

<DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>tab标签按需加载</title>
<style type="text/css">
ul li{width:200px; height:30px; float:left; list-style:none; text-align:center; border:1px solid gray; border-bottom:0;}
.on{border:1px solid pink; border-bottom:0; }
.hide{display:none;}
.tabDiv{width:800px; height:500px; clear:both;}
#tab1{line-height:25px; margin:50px 0 0 40px;}
#tab2 img{width:150px; height:150px;}
</style>
</head>
<body>
<ul>
<li class="on">全部</li>
<li id="viewTsb1" onclick="showTabContent();">指定商品折扣</li>
</ul>
<div class="tabDiv">
<div id="tab1">全部标签 应该展示全部内容</div>
<div id="tab2" class="hide">
<img data-src="http://img0.bdstatic.com/img/image/shouye/mxly.jpg" />
<img data-src="http://img0.bdstatic.com/img/image/shouye/mxjinxiuxian.jpg" />
</div>
</div>
<script type="text/javascript">
var isLoadedImg = false;
function showTabContent(){
if(isLoadedImg) return;
var tab2 = document.getElementById('tab2'),
tab1 = document.getElementById('tab1'),
aImg = tab2.getElementsByTagName('img'),
l = aImg.length;
tab1.className = 'hide';
tab2.className = '';
for(var i= 0; i<l; i++){
aImg[i].src = aImg[i].getAttribute('data-src');
}
isLoadedImg = true;
this.className = 'on';
this.parentNode.getElementsByTagName('li')[0].className = '';
}
</script>
</body>
</html>

运行上述代码并抓包并发现:一开始没有看到有图片的请求,但点击“指定商品折扣券”标签时,看到有图片的请求发送。需要注意的是,为了确保体验,首屏的图片不建议懒惰加载,而应该直接展示出来;避免一开始用户就无法看到图片,在IE下看到一个虚线框,这样体验反而不好。

~~~图片懒惰加载 原理都是在满足条件时,用img的data-src的值给src属性赋值,进行请求图片资源

按需执行JS

按需执行JS和懒惰加载图片比较类似。当打开网页时,如果等所有JS都加载并执行完毕,再把界面呈现给用户,这样整体上性能会比较慢,体验也不友好。就是当某个动作触发后,再执行相应的JS,以便来渲染界面。按需执行JS,可以应用在下列场景:执行一些耗时比较久的JS代码,或执行JS后,需要加载比较多图片、加载iframe、加载广告等。在一些webapp的应用中,或比较复杂的页面时,更加应该使用这种方法。

~~~延迟加载JS和按需加载JS两种应用场景

实现思路和按需加载比较类似:

  • 对滚动条进行事件绑定,假设绑定的函数为function lazyExecuteJS(){};
  • 在函数lazyExecuteJS中,按照下面思路实现:选择一个元素作为参照物,当滚动条即将靠近时该元素位置,开始执行对应的JS,从而实现对界面的渲染;

示例代码如下(以YUI3框架为例):

首先下载最近封装的异步滚动条加载组件:Y.asyncScrollLoader,然后运行下面的代码(需要把页面和Y.asyncScrollLoader.js 放在同一个目录):

运行上述代码并抓包发现:打开页面时,是不没有看到有对应的图片请求,但当滚动条拉到一定位置时,loadAD的函数被执行。

按需加载JS

JavaScript无非就是script标签引入页面,但当项目越来越大的时候,单页面引入N个js显然不行,合并为单个文件减少了请求数,但请求的文件体积却很大。这时候比较合理的做法就是按需加载。按需加载和按需执行JS比较类似,只不过要执行的JS变成了固定的“实现加载JS”的代码。按需加载实现的思路如下:

  • 对滚动条进行事件绑定,假设绑定的函数为function lazyLoadJS(){};
  • 在函数lazyLoadJS中,按照下面思路实现:选择一个元素作为参照物,当滚动条即将靠近时该元素位置,开始执行加载对应JS;
  • 在JS加载完毕后,开始执行相应的函数来渲染界面;
  • 在实际项目中,可以根据需要设置一个目标距离,比如还有200像素该元素即将进入可视区域;按需加载JS和按需执行JS比较类似,这里就不再单独提供示例代码了;大家可以在按需执行JS的中示例中,把loadAD函数更改为动态加载JS即可;

分屏展示

当一个网页比较长,有好几个屏幕,而且加载了大量的图片、广告等资源文件时,分屏展示,可提升页面性能和用户体验。其实分屏展示也可以从按需加载的的角度来看待,默认是加载第一屏幕的内容,当滚动条拉动即将到达下一个屏幕时,再开始渲染下个屏的内容。换言之,是把图片、背景图片、HTML一起按需加载,一开始不对HTML进行解析,那么背景图、img图片也不会进行加载。

分屏展示的思路如下:

  • 根据具体业务情况,收集主流最大的分辨率的高度;假设这里是用960px;
  • 按照这个高度进行分屏,依次把下一个屏幕内的HTML用来表示;
  • 为了让页面的高度不变,需要让textarea占据一定的页面空间,也就是让页面出现对应的滚动条;因此需要指定样式visility:hidden,并指定它的高度和宽度。
  • 利用上述讲的按需执行JS,把里面的HTML代码提取出来,重新填充到textarea的父节点上,便可实现解析对应HTML,从而实现分屏展示。

示例代码如下(需要把页面和Y.asyncScrollLoader.js 放在同一个目录):

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>分屏加载html</title>
<style type="text/css">
.page-hide{visibility:hidden; width:100%; height:300px; }
.veryhigh{height:1200px;}
</style>
</head>
<body>
<div class="veryhigh"><span>向下拖动滚动条 美图在下面</span></div>
<textarea>
<div>
<img src="http://img0.bdstatic.com/img/image/shouye/mxtianfuzhen.jpg" />
<span>我是按需加载进来的哦</span>
</div>
</textarea>
</body>
</html>

运行上面代码并抓包发现:在默认首屏,并没有去解析textarea里面的代码,但当拉动滚动条到一定位置时,textarea里面的HTML依次被解析,从而实现了网页分屏展示。

使用“按需加载”进行性能优化时,需要合理选择触发的动作。“按需加载”的最大优势在于减少了不必要的资源请求,节省流量,真正实现“按需所取”。但是“按需加载”本身如果使用不当也会影响用户体验,因为“按需加载”的时机在用户触发某动作之后,如果用户的网速比较慢的话,加载脚本或执行脚本可能需要等候较长的时间,而用户则不得不为此付出代价。因此,如果要使用“按需加载”则需要选择正确的触发动作,如果是根据滚动条来触发,可考虑一个目标距离,假设目标距离还有200像素即将进入可视区域,则就开始加载,而不是等到进入了可视区域才加载。以上所讲的各种“按需加载”类型,都可以封装成相应的组件,然后就可以在项目中进行应用。

转:按需加载html 图片 css js的更多相关文章

  1. 刚部署的程序加载不出来css,js以及图片

    刚部署的程序加载不出来css,js以及图片,解决方式 需要在配置中加入静态资源 方法一: controller.xml中加入 <mvc:annotation-driven/> <mv ...

  2. 转载 yii2-按需加载并管理CSS样式/JS脚本

    一.资源包定义 Yii2对于CSS/JS 管理,使用AssetBundle资源包类. 创建如下: backend/assets/AppAsset.php namespace backend\asset ...

  3. yii2.0 如何按需加载并管理CSS样式及JS脚本

    链接:http://www.yiichina.com/tutorial/399 (注:以下为Yii2.0高级应用测试) Yii2.0对于CSS/JS 管理,使用AssetBundle资源包类. 视图如 ...

  4. yii2-按需加载并管理CSS样式/JS脚本

    原文地址:https://segmentfault.com/a/1190000003742452

  5. 个人博客制作如何选择前端模板 thinkcmf后台加载新模板 CSS js文件

    我们的博客后台已经搭建好了,接下来我就要选择一个合适的模板做自己的博客,首先要定位你的博客是做什么用的,是属于什么行业,根据自己博客的定位选择适合的模板. 如果你是设计师,又会前端设计开发,那就可以自 ...

  6. webpack css模块化和ant-design按需加载冲突

    其实具体出现了什么问题,我也记得不清楚了,今天突然回想起来必须记录一下,一个思想就是用exclude将node_module目录下的文件排除,网上有很多相关例子,但不知是不是因为时间久远,都不生效,无 ...

  7. Vue性能优化之组件按需加载(以及一些常见的性能优化方法)

    关于Vue中的按需加载我就简单介绍一下:大概就是我们所有的东西都会在app.js里面,但是我们并不需要把所有的组件都一次性加载进来,我们可以在需要它的时候再将它加载进来,话不多说,开车! 1.webp ...

  8. 按需加载.js .css文件

    首先,理解按需加载当你需要用到某个js里面的函数什么鬼,或者某个css里的样式的时候你才开始加载这个文件. 然后是怎样实现的,简单来说就是在js中动态的createElem<script> ...

  9. 经验总结:按需加载JS和css

    项目中做过这样的事情:所有页面都通过SSI指令 include这样一份public-js.shtml, 用来引入涉及到的js(包括公共的脚本 验证插件 自定义组件等),但是一些没有交互效果的页面根本不 ...

随机推荐

  1. InnerException 消息是“反序列化对象 属于类型 *** 时出现错误。读取 XML 数据时,超出最大字符串内容长度配额 (8192)。(注意细节)

    WEB站点在调用我们WCF服务的时候,只要传入的参数过长,就报如下错误: 格式化程序尝试对消息反序列化时引发异常: 尝试对参数 http://tempuri.org/ 进行反序列化时出错: formD ...

  2. iOS加载HTML, CSS代码

    NSString *strHTML = @"<div style=\"text-align:center;\"><img src=\"/Upl ...

  3. iOS 相互引用引起内存泄露问题说明

    release动作只会对自身计数减一,而不会对属性发出release消息,只有该对象的引用计数为0,系统才会对该对象的所有属性发出release消息 类A的属性有类B,类B的属性有类A,而且两者都是强 ...

  4. HDU 4861 Couple doubi(找规律|费马定理)

    Couple doubi Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit ...

  5. 使用 IObjectSafety 标记 ATL 控件初始化的安全

    MSDN原文.这里我将代码使用到了BHO里面,运行调试没问题.拿来分享一下 概要 您可以使用 IObjectSafetyImpl 的默认实现来标记为可安全执行脚本的控件.在许多情况下,您需要将标记为可 ...

  6. Exponentiation

    Description Problems involving the computation of exact values of very large magnitude and precision ...

  7. Eclipse 实现关键字自动补全功能 (转)

    一般默认情况下,Eclipse ,MyEclipse 的代码提示功能是比Microsoft Visual Studio的差很多的,主要是Eclipse ,MyEclipse本身有很多选项是默认关闭的, ...

  8. SVN 无法连接主机:由于目标计算机积极拒绝,无法连接

    问题:使用追溯功能时因为时间太长,所以强行关闭了SVN;当再次连接的时候就发现不能连接到SVN了,错误消息: 无法连接主机:由于目标计算机积极拒绝,无法连接 解决:重启一下服务器的SVN 服务就可以了

  9. javascript模块化编程(转载)

    Javascript 模块化编程 作者: 阮一峰  发布时间: 2013-01-08 18:04  阅读: 7632 次  推荐: 40   原文链接   [收藏]   随着网站逐渐变成"互 ...

  10. JS中的内存泄漏

    明天下午面试微店的前端开发职位,有点紧张~~ 刚刚正好看到js中的内存泄露,所以来整理一番. 给DOM对象添加的属性是对一个js对象的引用. var MyObject = {}; document.g ...