demo

若果要做多少个一体化的 BigRender demo,恐怕相比较复杂,还要涉及到后端。

前边学习 lazyload 时做过二个图片的延迟加载 demo,see
http://hanzichi.github.io/2015/picture-lazyload/。因为
BigRender 是 lazyload 的加强版,所以轻松地做了个 BigRender
版本的图形延迟加载
http://hanzichi.github.io/2016/bigrender/,落成的具体代码可以check
。求
star,求 fork~

BigRender

当三个网址越来越粗大,加载速度更是慢的时候,开辟者们只好对其开展优化,什么人愿意采访叁个内需等待
10 秒,20 秒技艺出现的网页呢?

科学普及的也是周旋简便易行易行的四个优化方案是
图形的延期加载。八个急剧的页面,临时我们并不会滚动去看上边包车型地铁源委,那样就浪费了非首屏部分的渲染,而那一个不算的渲染,不止囊括图片,还包罗别的的
DOM 元素,以至部分 js/css(有些js/css 是基于模块央求的,比方有个别ajax),理论上,每扩展贰个 DOM,都会大增渲染的光阴。有未有法子能使得
HTML、js、css 都能按需加载呢?答案是早晚的,那正是本文要讲的 BigRender。

产业界有成都百货上千 BigRender 在生养景况中的案例,比方
新浪,美团,乐途游历网,360网站导航,天猫商城商品详细情况页
等等。查看它们的源代码(ctrl+u),ctrl+f 寻找 textarea
关键字,很轻便能够看来有的被 textarea 标签包裹的 HTML 代码。

诸如马蜂窝:

凯旋门074网址 1

而那么些被 textarea 标签包裹的 HTML 代码,只是 textarea 的 value
值,并不曾渲染到 DOM 树上。没有错,BigRender 通常正是用 textarea 标签包裹
HTML 代码(js/css),充作其 value 值,等到合适的机缘(日常当 textarea
标签出现照旧将要现身在客户视界时)将 textarea 中的 HTML 代码抽取,用
innerHTML 动态插入到 DOM 树中,如有供给,抽出 js/css
代码(正则),动态实施它们。(是还是不是和图表的延迟加载很平日?)

玉伯提议:

页面下载实现后,要通过 Tokenization — Tree Construction — Rendering.
要让首屏尽快出来,得给浏览器缓慢化解渲染首屏的专门的学问量。能够从双方面入手: 1.
调整和减少 DOM 节点数。节点数越少,意味着 Tokenization, Rendering
等操作消耗的时光越少。(对于标准的天猫商城商品实际情况页,经测验发掘,每扩大三个DOM 节点,会产生首屏渲染时间推移约 0.5ms.) 2.
回退脚本推行时间。脚本试行和 UI Update 分享二个 thread,
脚本耗的时日越少,UI Update 就能够特别提前。

1
2
3
4
5
页面下载完毕后,要经过 Tokenization — Tree Construction — Rendering. 要让首屏尽快出来,得给浏览器减轻渲染首屏的工作量。可以从两方面入手:
 
  1. 减少 DOM 节点数。节点数越少,意味着 Tokenization, Rendering 等操作耗费的时间越少。(对于典型的淘宝商品详情页,经测试发现,每增加一个 DOM 节点,会导致首屏渲染时间延迟约 0.5ms.)
 
  2. 减少脚本执行时间。脚本执行和 UI Update 共享一个 thread, 脚本耗的时间越少,UI Update 就能越发提前。

为什么是用 textarea 标签贮存大块 HTML 内容?还是能看下玉伯的
那篇文章。Taobao的
kissy 就停放了 DataLazyload 组件。(插播:美团详细情形页还也许有使用 script
标签做 BigRender 优化,详细情况请见上边包车型大巴 “其余” 一节)

接下去就来一步一步达成八个顺应自身的 BigRender 插件,小编盼望得以顺延加载
HTML 成分、js 以及 css。

evalScripts/evalStyles

剧本试行,样式渲染。

澳门凯旋门游戏网址 ,evalScripts: function(code) { var head =
doc.getElementsByTagName(“head”)[0] , js =
doc.createElement(“script”); js.text = code; head.insertBefore(js,
head.firstChild); head.removeChild(js); }, evalStyles: function(code) {
var head = doc.getElementsByTagName(“head”)[0] , css =
doc.createElement(“style”); css.type = “text/css”; try {
css.appendChild(doc.createTextNode(code)); } catch (e) {
css.styleSheet.cssText = code; } head.appendChild(css); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
evalScripts: function(code) {
  var head = doc.getElementsByTagName("head")[0]
    , js = doc.createElement("script");
 
  js.text = code;
  head.insertBefore(js, head.firstChild);
  head.removeChild(js);
},
 
evalStyles: function(code) {
  var head = doc.getElementsByTagName("head")[0]
    , css = doc.createElement("style");
 
  css.type = "text/css";
  try {
    css.appendChild(doc.createTextNode(code));
  } catch (e) {
    css.styleSheet.cssText = code;
  }
  head.appendChild(css);
}

打赏扶助自个儿写出更加的多好作品,多谢!

凯旋门074网址 2

1 赞 5 收藏
评论

T.datalazyload

效仿 jQuery 的写法笔者定义了一个大局对象 T,将顺延加载的完结代码封装在了
T.datalazyload 对象中,将急需延期加载的代码 “包裹” 在 textarea
标签中,设置其 visibility 属性为
hidden,并给予该标签一个非正规的类名(为了做事件监听),比方叫做
“datalazyload”。为了有匡助,笔者分明各类做 bigrender 优化的 textarea
的父节点都独有二个子孩子(即该 textarea
成分)
,那点拾叁分关键必得服从,因为后边代码有指向此的至极管理。(注意要安装好父节点的莫大大幅度,和
dom 渲染后的惊人小幅度保持一致)

一对 HTML/js/css 代码都足以打包在 textarea 标签中,比如:

XHTML

凯旋门074网址 ,<textarea class=”datalazyload” style=”visibility: hidden;”>
<script type=”text/javascript”> alert(“I am lazyload zone!”);
</script> <style type=”text/css”> .main {margin: 0 auto;
text-align: center; padding-top: 200px; width:一千px; height:一千px;
border:5px black dashed;} .second {margin: 0 auto; width:一千px;
height:200px; border: 5px purple dotted; padding-top: 100px; text-align:
center;} </style> <div class=”second”>
<h1>作者是延迟加载的有的!</h1> </div> </textarea>

1
2
3
4
5
6
7
8
9
10
11
12
13
<textarea class="datalazyload" style="visibility: hidden;">
  <script type="text/javascript">
    alert("I am lazyload zone!");
  </script>
 
  <style type="text/css">
    .main {margin: 0 auto; text-align: center; padding-top: 200px; width:1000px; height:1000px; border:5px black dashed;}
    .second {margin: 0 auto; width:1000px; height:200px; border: 5px purple dotted; padding-top: 100px; text-align: center;}
  </style>
  <div class="second">
    <h1>我是延迟加载的部分!</h1>
  </div>
</textarea>

澳门凯旋门注册网址 ,关于作者:韩子迟

凯旋门074网址 3

a JavaScript beginner
个人主页 ·
小编的作品 ·
9 ·
   

凯旋门074网址 4

pollTextarea

pollTextareas: function() { // 需延迟加载的元素已经全副加载完 if
(!this.els.length) { T.removeEvent(window, “scroll”, this.fn);
T.removeEvent(window, “resize”, this.fn); T.removeEvent(doc.body,
“touchMove”, this.fn); return; } // 决断是或不是须求加载 for (var i =
this.els.length; i–; ) { var ele = this.els[i]; if
(!this.inView(ele)) continue; this.insert(ele); this.els.splice(i, 1); }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pollTextareas: function() {
 
  // 需延迟加载的元素已经全部加载完
  if (!this.els.length) {
    T.removeEvent(window, "scroll", this.fn);
    T.removeEvent(window, "resize", this.fn);
    T.removeEvent(doc.body, "touchMove", this.fn);
    return;
  }
 
  // 判断是否需要加载
  for (var i = this.els.length; i–; ) {
    var ele = this.els[i];
 
    if (!this.inView(ele))
      continue;
 
    this.insert(ele);
    this.els.splice(i, 1);
  }
}

本条点子的效应是判定必要延期加载的因素是不是曾在视口,假设是,则开展加载(触发
insert
方法),并且在数组中剔除该因素;假使数组为空,则表明必要延期加载的一些都早已加载完,移除事件监听,整个延迟加载甘休。

init

给 T.datalazyload 对象定义二个 init() 方法,开始化页面时监听
scroll、resize 以及运动端的 touchmove
事件,当接触那些事件时,回调函数内剖断延迟加载部分是还是不是已经出现在视口。

明令防止转发。init: function(config) { var cls = config.cls; this.threshold =
config.threshold ? config.threshold : 0; this.els =
Array.prototype.slice.call(T.getElementsByClassName(cls)); this.fn =
this.pollTextareas.bind(this); this.fn(); T.addEvent(window, “scroll”,
this.fn); T.addEvent(window, “resize”, this.fn); T.addEvent(doc.body,
“touchMove”, this.fn); }

1
2
3
4
5
6
7
8
9
10
11
12
init: function(config) {
  var cls = config.cls;
  this.threshold = config.threshold ? config.threshold : 0;
 
  this.els = Array.prototype.slice.call(T.getElementsByClassName(cls));
  this.fn = this.pollTextareas.bind(this);
 
  this.fn();
  T.addEvent(window, "scroll", this.fn);
  T.addEvent(window, "resize", this.fn);
  T.addEvent(doc.body, "touchMove", this.fn);
}

config 是安顿参数,其 cls 属性表示供给延期加载的 textarea
的类名,threshold 为阈值,单位 px,表示当 textarea
距离视口多少像素时,进行预加载。

将索要延期加载的元素存入二个数组(this.els),(某 textarea
成分)后续一旦完成加载随即在数组中除去该因素。事件监听的回调函数为
pollTextarea() 方法。

利弊 & 适用场景

归纳讲讲 BigRender 优化的优劣势,以及适用场景。

亮点很鲜明,因为压缩了首屏 DOM
的渲染,所以能加速首屏加载的速度,并且能分块加载
js/css,极其适用于有个别模块区分度极高的网址(个人认为大型网址的模块区分度普及更加高了)。

症结是急需改动 DOM 结构(DOM
节点的替换和渲染),只怕会引起局地重排和重绘。一些尚无拉开 js
成效的客商将看不到延迟加载的从头到尾的经过(能够用 noscript
标签给出三个好意提醒)。最大的缺陷恐怕是不便宜 SEO,一些借助于 SEO
的网址或然要求在 SEO 上下点武术了,比方美团。

关于 SEO,能够看下
那么些网站,能模仿搜索引擎蜘蛛对网址的爬取景况。美团对于 BigRender 以及
SEO 实施方案
[美团网案例]勘误BigRender技能导致的SEO难题

bigrender 通过压缩 DOM
节点,加速首屏的渲染,但是,它也会有特别的属性损耗的,渲染前textarea
里面包车型地铁 html 代码,在服务端把 html 代码保存在隐敝的 textarea
里面,所以在服务端会把 html
代码转义:尖括号等都被转义了,这么些会增添服务器的下压力;并且,那一个退换只是前面叁个的渲染,服务器依然是贰回总计有所的数量,输出全体的数码,那点并未有获取提升。

日常的话,使用都是往端拼接成 html 字符串,然后塞入 textarea
标签中,吐给前端。

明令防止转发。大流量网站品质优化:一步一步创设一个切合自身的BigRender插件

2016/05/19 · JavaScript
· 插件

明令防止转发。本文小编: 伯乐在线 –
韩子迟
。未经小编许可,禁绝转发!
招待参预伯乐在线 专辑作者。

其他

除开头页部分用了 textarea 做 BigRender 优化外,美团还用到了 script
标签做优化。举个例子
本条商品详细情形页

凯旋门074网址 5

给 script 标签设置个非 “text/javascript” 的 type,能够下载这段
js,但不进行,这种做法似曾相识,在 labjs 中看见过。

越多能够参照他事他说加以考察
后边二个优化三续:用script寄放html代码来裁减DOM节点数

Read More

  • 天猫商城详细情形页的 BigRender 优化与贮存大块 HTML
    内容的最棒艺术(推荐!!如果被Q了可以
    看这里)
  • 前端优化:BigRender的textarea延迟渲染和有关LABjs的实行
  • lazyload延迟加载组件
  • KISSY懒加载data lazyload
    的应用
  • kissy datalazyload.js
    源码
  • kissy DataLazyload
    API
  • kissy DataLazyload
    demos

打赏协助我写出越多好文章,谢谢!

打赏小编

(借使以为楼主的稿子对您有赞助,请关怀楼主的 Github )

insert

接下去看 insert 方法。inert 方法的参数是急需延期加载的 textarea
元素,很显明,我们必要深入分析的代码全在 textarea.innerHTML 中。大家用
extractCode 方法收取其中的 js/css 代码,然后将 js/css
过滤掉,那样多余的就全部都以 HTML 代码了,将其插入 DOM 中(那正是前文说的
“各类 textarea 的父节点都独有三个子孩子” 的缘由,能够一向用父节点
innerHTML 操作),借使有 loading 效果,平时在父节点加个 loading
类,移除就能够。最终再动态实行 js 脚本,插入 css 样式。

insert: function(ele) { var parent = ele.parentNode , txt =
this.decodeHTML(ele.innerHTML) , matchStyles = this.extractCode(txt,
true) , matchScripts = this.extractCode(txt); parent.innerHTML = txt
.replace(new
RegExp(“<script[^>]*>([\\S\\s]*?)</script\\s*>”,
“img”), “”) .replace(new
RegExp(“<style[^>]*>([\\S\\s]*?)</style\\s*>”,
“img”), “”); if (matchStyles.length) for (var i = matchStyles.length; i
–;) this.evalStyles(matchStyles[i]); // 假使推迟部分须要做 loading
效果 parent.className = parent.className.replace(“loading”, “”); if
(matchScripts.length) for (var i = 0, len = matchScripts.length; i <
len; i++) this.evalScripts(matchScripts[i]); },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
insert: function(ele) {
  var parent = ele.parentNode
    , txt = this.decodeHTML(ele.innerHTML)
    , matchStyles = this.extractCode(txt, true)
    , matchScripts = this.extractCode(txt);
 
  parent.innerHTML = txt
    .replace(new RegExp("<script[^>]*>([\\S\\s]*?)</script\\s*>", "img"), "")
    .replace(new RegExp("<style[^>]*>([\\S\\s]*?)</style\\s*>", "img"), "");
 
  if (matchStyles.length)
    for (var i = matchStyles.length; i –;)
      this.evalStyles(matchStyles[i]);
 
  // 如果延迟部分需要做 loading 效果
  parent.className = parent.className.replace("loading", "");
 
  if (matchScripts.length)
    for (var i = 0, len = matchScripts.length; i < len; i++)
      this.evalScripts(matchScripts[i]);
},

extractCode

作者们透过正则将 js 和 css 标签部分收取:

extractCode: function(str, isStyle) { var cata = isStyle ? “style” :
“script” , scriptFragment = “<” + cata +
“[^>]*>([\\S\\s]*?)</” + cata + “\\s*>” ,
matchAll = new RegExp(scriptFragment, “img”) , matchOne = new
RegExp(scriptFragment, “im”) , matchResults = str.match(matchAll) ||
[] , ret = []; for (var i = 0, len = matchResults.length; i <
len; i++) { var temp = (matchResults[i].match(matchOne) || [ “”, “”
])[1]; temp && ret.push(temp); } return ret; }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extractCode: function(str, isStyle) {
  var cata = isStyle ? "style" : "script"
    , scriptFragment = "<" + cata + "[^>]*>([\\S\\s]*?)</" + cata + "\\s*>"
    , matchAll = new RegExp(scriptFragment, "img")
    , matchOne = new RegExp(scriptFragment, "im")
    , matchResults = str.match(matchAll) || []
    , ret = [];
 
  for (var i = 0, len = matchResults.length; i < len; i++) {
    var temp = (matchResults[i].match(matchOne) || [ "", "" ])[1];
    temp && ret.push(temp);
  }
  return ret;
}

工作有成地将 script 以及 style
标签内的内容提取了出去,奇妙地用了正则中的子表明式。

相关文章