俄罗斯贵宾会-俄罗斯贵宾会官网
做最好的网站

大流量网站性能优化:一步一步打造一个适合自己的BigRender插件(转)

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() 方法。

其他

除了首页部分用了 textarea 做 BigRender 优化外,美团还用到了 script 标签做优化。比如 这个商品详情页

图片 1

给 script 标签设置个非 "text/javascript" 的 type,可以下载这段 js,但不执行,这种做法似曾相识,在 labjs 中看到过。

更多可以参考 前端优化三续:用script存放html代码来减少DOM节点数

Read More

打赏支持我写出更多好文章,谢谢!

打赏作者

Read More

http://www.cnblogs.com/zichi/p/5252629.html

BigRender

当一个网站越来越庞大,加载速度越来越慢的时候,开发者们不得不对其进行优化,谁愿意访问一个需要等待 10 秒,20 秒才能出现的网页呢?

常见的也是相对简单易行的一个优化方案是 图片的延迟加载。一个庞大的页面,有时我们并不会滚动去看下面的内容,这样就浪费了非首屏部分的渲染,而这些无用的渲染,不仅包括图片,还包括其他的 DOM 元素,甚至一些 js/css(某些js/css 是根据模块请求的,比如一些 ajax),理论上,每增加一个 DOM,都会增加渲染的时间。有没有办法能使得 HTML、js、css 都能按需加载呢?答案是肯定的,这就是本文要讲的 BigRender。

业界有很多 BigRender 在生产环境中的案例,比如 新浪美团途牛旅行网360网址导航淘宝商品详情页 等等。查看它们的源代码(ctrl+u),ctrl+f 搜索 textarea 关键字,很容易可以看到一些被 textarea 标签包裹的 HTML 代码。

比如途牛:

图片 2

而这些被 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 内容?还是可以看下玉伯的 这篇文章。淘宝的 kissy 就内置了 DataLazyload 组件。(插播:美团详情页还有用到 script 标签做 BigRender 优化,详情请见下面的 “其他” 一节)

接下去就来一步一步实现一个适合自己的 BigRender 插件,我希望可以延迟加载 HTML 元素、js 以及 css。

BigRender

当一个网站越来越庞大,加载速度越来越慢的时候,开发者们不得不对其进行优化,谁愿意访问一个需要等待 10 秒,20 秒才能出现的网页呢?

常见的也是相对简单易行的一个优化方案是 图片的延迟加载。一个庞大的页面,有时我们并不会滚动去看下面的内容,这样就浪费了非首屏部分的渲染,而这些无用的渲染,不仅包括图片,还包括其他的 DOM 元素,甚至一些 js/css(某些js/css 是根据模块请求的,比如一些 ajax),理论上,每增加一个 DOM,都会增加渲染的时间。有没有办法能使得 HTML、js、css 都能按需加载呢?答案是肯定的,这就是本文要讲的 BigRender。

业界有很多 BigRender 在生产环境中的案例,比如 新浪美团途牛旅行网360网址导航淘宝商品详情页 等等。查看它们的源代码(ctrl+u),ctrl+f 搜索 textarea 关键字,很容易可以看到一些被 textarea 标签包裹的 HTML 代码。

比如途牛:

图片 3

而这些被 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 就能越发提前。

为什么是用 textarea 标签存放大块 HTML 内容?还是可以看下玉伯的 这篇文章。淘宝的 kissy 就内置了 DataLazyload 组件。(插播:美团详情页还有用到 script 标签做 BigRender 优化,详情请见下面的 "其他" 一节)

接下去就来一步一步实现一个适合自己的 BigRender 插件,我希望可以延迟加载 HTML 元素、js 以及 css。

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 标签内的内容提取了出来,巧妙地用了正则中的子表达式。

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]);
},

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);
}

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);
}

config 是配置参数,其 cls 属性表示需要延迟加载的 textarea 的类名,threshold 为阈值,单位 px,表示当 textarea 距离视口多少像素时,进行预加载。

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

大流量网站性能优化:一步一步打造一个适合自己的BigRender插件

2016/05/19 · JavaScript · 插件

本文作者: 伯乐在线 - 韩子迟 。未经作者许可,禁止转载!
欢迎加入伯乐在线 专栏作者

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);
}

(如果觉得楼主的文章对你有帮助,请关注楼主的 Github https://github.com/hanzichi

demo

如果要做一个完整的 BigRender demo,可能比较复杂,还要涉及到后端。

之前学习 lazyload 时做过一个图片的延迟加载 demo,see http://hanzichi.github.io/cnblogs/2016/Feb/picture-lazyload/。因为 BigRender 是 lazyload 的加强版,所以简单地做了个 BigRender 版本的图片延迟加载 http://hanzichi.github.io/cnblogs/2016/Mar/bigrender/,实现的具体代码可以 check bigrender.js。求 star,求 fork~

其他

除了首页部分用了 textarea 做 BigRender 优化外,美团还用到了 script 标签做优化。比如 这个商品详情页

图片 4

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

更多可以参考 前端优化三续:用script存放html代码来减少DOM节点数

T.datalazyload

仿照 jQuery 的写法我定义了一个全局对象 T,将延迟加载的实现代码封装在了 T.datalazyload 对象中,将需要延迟加载的代码 "包裹" 在 textarea 标签中,设置其 visibility 属性为 hidden,并赋予该标签一个特殊的类名(为了做事件监听),比如叫做 "datalazyload"。为了方便,我规定每个做 bigrender 优化的 textarea 的父节点都只有一个子孩子(即该 textarea 元素),这一点非常重要必须遵守,因为后面代码有针对此的特殊处理。(注意要设置好父节点的高度宽度,和 dom 渲染后的高度宽度保持一致)

一些 HTML/js/css 代码都可以包裹在 textarea 标签中,例如:

<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>

本文由俄罗斯贵宾会发布于Web前端,转载请注明出处:大流量网站性能优化:一步一步打造一个适合自己的BigRender插件(转)

您可能还会对下面的文章感兴趣: