zoukankan      html  css  js  c++  java
  • 转:按需加载html 图片 css js

    按需加载是前端性能优化中的一项重要措施,按需加载是如何定义的呢?顾名思义,指的是当用户触发了动作时才加载对应的功能。触发的动作,是要看具体的业务场景而言,包括但不限于以下几个情况:鼠标点击、输入文字、拉动滚动条,鼠标移动、窗口大小更改等。加载的文件,可以是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>

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

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

    tab

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

    •  生成<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像素即将进入可视区域,则就开始加载,而不是等到进入了可视区域才加载。以上所讲的各种“按需加载”类型,都可以封装成相应的组件,然后就可以在项目中进行应用。

  • 相关阅读:
    C# 窗体间传值方法大汇总(转)
    STM32 配置PC13~PC15
    STM32的USART发送数据时如何使用TXE和TC标志
    STM32_NVIC寄存器详解
    protel99se 问题汇总(不定期更新)
    STM32串口IAP实验笔记
    Keil MDK下如何设置非零初始化变量(复位后变量值不丢失)
    STM32定时器配置(TIM1-TIM8)高级定时器+普通定时器,定时计数模式下总结
    帮助类-AD域操作
    GitHub贡献第一的公司是谁?微软开源软件列表
  • 原文地址:https://www.cnblogs.com/stephenykk/p/3642219.html
Copyright © 2011-2022 走看看