当一个网站越来越庞大,加载速度越来越慢的时候,开发者们不得不对其进行优化,谁愿意访问一个需要等待 10 秒,20 秒才能出现的网页呢?
常见的也是相对简单易行的一个优化方案是 图片的延迟加载。一个庞大的页面,有时我们并不会滚动去看下面的内容,这样就浪费了非首屏部分的渲染,而这些无用的渲染,不仅包括图片,还包括其他的 DOM 元素,甚至一些 js/css(某些js/css 是根据模块请求的,比如一些 ajax),理论上,每增加一个 DOM,都会增加渲染的时间。有没有办法能使得 HTML、js、css 都能按需加载呢?答案是肯定的,这就是本文要讲的 。
业界有很多 在生产环境中的案例,比如 新浪,美团,途牛旅行网,360网址导航,淘宝商品详情页 等等。查看它们的源代码(ctrl+u),ctrl+f 搜索 关键字,很容易可以看到一些被 标签包裹的 HTML 代码。
比如途牛:
而这些被 标签包裹的 HTML 代码,只是 的 value 值,并没有渲染到 DOM 树上。没错, 通常就是用 标签包裹 HTML 代码(js/css),当作其 value 值,等到合适的时机(通常当 标签出现或者即将出现在用户视野时)将 中的 HTML 代码取出,用 动态插入到 DOM 树中,如有必要,取出 js/css 代码(正则)淘宝导航样式css代码,动态执行它们。(是不是和图片的延迟加载很相似?)
玉伯指出:
页面下载完毕后,要经过 — Tree — . 要让首屏尽快出来,得给浏览器减轻渲染首屏的工作量。可以从两方面入手:
1. 减少 DOM 节点数。节点数越少,意味着 , 等操作耗费的时间越少。(对于典型的淘宝商品详情页,经测试发现,每增加一个 DOM 节点,会导致首屏渲染时间延迟约 0.5ms.)
2. 减少脚本执行时间。脚本执行和 UI 共享一个 , 脚本耗的时间越少淘宝导航样式css代码 大流量网站性能优化:一步一步打造一个适合自己的BigRender插件,UI 就能越发提前。
为什么是用 标签存放大块 HTML 内容?还是可以看下玉伯的 这篇文章。淘宝的 kissy 就内置了 组件。(插播:美团详情页还有用到 标签做 优化,详情请见下面的 “其他” 一节)
接下去就来一步一步实现一个适合自己的 插件,我希望可以延迟加载 HTML 元素、js 以及 css。
T.
仿照 的写法我定义了一个全局对象 T,将延迟加载的实现代码封装在了 T. 对象中,将需要延迟加载的代码 “包裹” 在 标签中,设置其 属性为 ,并赋予该标签一个特殊的类名(为了做事件监听),比如叫做 “”。为了方便,我规定每个做 优化的 的父节点都只有一个子孩子(即该 元素),这一点非常重要必须遵守,因为后面代码有针对此的特殊处理。(注意要设置好父节点的高度宽度,和 dom 渲染后的高度宽度保持一致)
一些 HTML/js/css 代码都可以包裹在 标签中,例如:
init
给 T. 对象定义一个 init() 方法,初始化页面时监听 、 以及移动端的 事件淘宝导航样式css代码,当触发这些事件时,回调函数内判断延迟加载部分是否已经出现在视口。
init: (){
= .cls;
this. = .?. : 0;
this.els = Array..slice.call(T.me(cls));
this.fn = this..bind(this);
this.fn();
T.(,"",this.fn);
T.(,"",this.fn);
T.(doc.body,"",this.fn);
}
是配置参数,其 cls 属性表示需要延迟加载的 的类名, 为阈值,单位 px,表示当 距离视口多少像素时,进行预加载。
将需要延迟加载的元素存入一个数组(this.els),(某 元素)后续一旦完成加载随即在数组中删除该元素。事件监听的回调函数为 () 方法。
: (){
// 需延迟加载的元素已经全部加载完
if(!this.els.){
T.(,"",this.fn);
T.(,"",this.fn);
T.(doc.body,"",this.fn);
;
}
// 判断是否需要加载
for(vari = this.els.;i--;){
= this.els[i];
if(!this.(ele))
;
this.(ele);
this.els.(i,1);
}
}
这个方法的作用是判断需要延迟加载的元素是否已经在视口,如果是,则进行加载(触发 方法),并且在数组中删除该元素;如果数组为空,则表明需要延迟加载的部分都已经加载完,移除事件监听,整个延迟加载结束。
接下去看 方法。inert 方法的参数是需要延迟加载的 元素,很显然,我们需要解析的代码全在 . 中。我们用 方法取出其中的 js/css 代码,然后将 js/css 过滤掉,这样剩下的就全是 HTML 代码了,将其插入 DOM 中(这正是前文说的 “每个 的父节点都只有一个子孩子” 的原因,可以直接用父节点 操作),如果有 效果,一般在父节点加个 类,移除即可。最后再动态执行 js 脚本,插入 css 样式。
: (ele){
= ele.
,txt = this.(ele.)
, = this.(txt,true)
, = this.(txt);
. = txt
.(("
文章来源:http://mp.weixin.qq.com/s?src=3×tamp=1679475891&ver=1&signature=5Ejvg8tOKvy-zarozxWOzXLxweVY8Ot7vIUv9BmqDno7t4sf6LQ-SUs8FJad0JQVOyg1hCmGdXZ3MqoCYI-IiQC*DGBIul9LERswjMjoX07LzUIY-y2Y9SPY0B7xW0WN8jS9Q6hAcJFHShGQYFVVMURpTZ60n0ocTxx46O4=