High-traffic website performance optimization: step by step to create a suitable BigRender plug-in

BigRender

When a website gets bigger and bigger and its loading speed gets slower and slower, developers have to optimize it. Who wants to visit a webpage that takes 10 seconds and 20 seconds to appear?

A common and relatively simple optimization solution is lazy loading of pictures. A huge page, sometimes we don’t scroll to see the content below, which wastes the rendering of the non-first screen part, and these useless renderings include not only pictures, but also other DOM elements, and even some js/css (Some js/css are requested based on modules, such as some ajax). In theory, each additional DOM will increase the rendering time. Is there a way to make HTML, js, css can be loaded on demand? The answer is yes, this is BigRender that this article will talk about.

There are many cases of BigRender in the production environment in the industry, such as Sina, Meituan, Tuniu Travel, 360 website navigation, Taobao product details page, and so on. Check their source code (ctrl+u), ctrl+f search for textarea keywords, it is easy to see some HTML code wrapped by textarea tags.

For example, Tuniu:

High-traffic website performance optimization: step by step to create a suitable BigRender plug-in

The HTML code wrapped by the textarea tag is just the value of the textarea and is not rendered on the DOM tree. That's right, BigRender usually wraps HTML code (js/css) with textarea tags, as its value value, and waits for the right time (usually when the textarea tag appears or is about to appear in the user's field of view) to take out the HTML code in the textarea. Use innerHTML to dynamically insert into the DOM tree, if necessary, take out the js/css code (regular) and execute them dynamically. (Is it similar to lazy loading of pictures?)

Uncle Yu pointed out:

After the page is downloaded, it needs to go through Tokenization — Tree Construction — Rendering. To make the first screen appear as soon as possible, the browser has to reduce the workload of rendering the first screen. You can start from two aspects:

  1. Reduce the number of DOM nodes. The smaller the number of nodes, the less time it takes for operations such as tokenization and rendering. (For a typical Taobao product detail page, after testing, it is found that each additional DOM node will cause the first screen rendering time to be delayed by about 0.5ms.)

  2. Reduce script execution time. Script execution and UI Update share a thread. The less time the script takes, the faster UI Update can be.

Why use textarea tags to store large chunks of HTML content? You can still read this article by Uncle Yu. Taobao's kissy has built-in DataLazyload component. (Interlude: Meituan's details page also uses the script tag for BigRender optimization. For details, please see the "Other" section below)

Next, I will implement a BigRender plug-in that suits me step by step. I hope I can delay loading HTML elements, js and css.

T.datalazyload

I defined a global object T, imitating the way jQuery writes, and encapsulated the implementation code of lazy loading in the T.datalazyload object, and "wrapped" the code that needs lazy loading in the textarea tag, set its visibility attribute to hidden, and assign The tag has a special class name (for event monitoring), such as "datalazyload". For convenience, I stipulate that each parent node of a textarea optimized by bigrender has only one child (that is, the textarea element). This is very important and must be observed, because the code behind has special processing for this. (Note that the height and width of the parent node should be set to be consistent with the height and width after dom rendering)

Some HTML/js/css codes can be wrapped in textarea tags, for example:


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

init

Define an init() method for the T.datalazyload object. When the page is initialized, it listens to scroll, resize, and touchmove events on the mobile terminal. When these events are triggered, the callback function determines whether the delayed loading part has appeared in the viewport.


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 is a configuration parameter, and its cls attribute represents the class name of the textarea that needs to be lazily loaded. Threshold is the threshold. The unit is px, which indicates how many pixels the textarea is from the viewport before preloading.

Store the element that needs to be loaded with delay in an array (this.els), and delete the element from the array once the subsequent loading is completed (a textarea element). The callback function for event monitoring is the pollTextarea() method.

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

  }

}

The function of this method is to determine whether the element that needs to be lazily loaded is already in the viewport, if it is, load it (triggering the insert method), and delete the element in the array; if the array is empty, it indicates that the parts that need to be lazily loaded are all After loading, the event listener is removed, and the entire delayed loading is over.

insert

Next look at the insert method. The parameter of the inert method is the textarea element that needs to be loaded lazily. Obviously, the code we need to parse is all in textarea.innerHTML. We use the extractCode method to take out the js/css code, and then filter out the js/css, so that the rest is all HTML code, and insert it into the DOM (this is exactly the "parent node of each textarea" mentioned earlier Because there is only one child", you can directly use the parent node innerHTML operation). If there is a loading effect, generally add a loading class to the parent node and remove it. Finally, dynamically execute the js script and insert the css style.


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

We take out the js and css tags through regular rules:


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;

}

Successfully extracted the content in the script and style tags, and cleverly used the sub-expression in the regular.


evalScripts/evalStyles

Script execution, style rendering.


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

}

Advantages and disadvantages & applicable scenarios

Briefly talk about the advantages and disadvantages of BigRender optimization and the applicable scenarios.

The advantages are obvious. Because the rendering of the first screen DOM is reduced, the loading speed of the first screen can be accelerated, and js/css can be loaded in blocks, which is very suitable for some websites with a high degree of module differentiation (I personally feel that the module differentiation of large websites The degree is generally getting higher and higher).

The disadvantage is the need to change the DOM structure (the replacement and rendering of DOM nodes), which may cause some rearrangement and redrawing. Some users who do not enable the js function will not see the delayed loading content (you can use the noscript tag to give a kind reminder). The biggest disadvantage may be that it is not conducive to SEO. Some websites that rely on SEO may need to work hard on SEO, such as Meituan.

Regarding SEO, you can look at the website http://www.seoqx.com/lynx , which can simulate the crawling of the website by search engine spiders. Meituan for BigRender and SEO solutions [Meituan.com case] Improve SEO problems caused by BigRender technology

bigrender speeds up the rendering of the first screen by reducing DOM nodes, but it also has additional performance loss. The html code in the textarea before rendering is stored in the hidden textarea on the server side, so it will be stored on the server side. HTML code escape: Angle brackets and so on have been escaped, this will increase the pressure on the server; moreover, this transformation is only front-end rendering, the server still calculates all data and outputs all data at once, this has not been improved.

Generally speaking, the backend is spliced ​​into html strings, then stuffed into the textarea tag, and spit out to the front end.

demo

If you want to make a complete BigRender demo, it may be more complicated and involves the back end.

When I was learning lazyload before, I did a picture lazyloading demo, see http://hanzichi.github.io/2015/picture-lazyload/. Because BigRender is an enhanced version of lazyload, I simply made a BigRender version of image lazy loading http://hanzichi.github.io/2016/bigrender/, the specific code can be checked https://github.com/hanzichi /hanzichi.github.io/blob/master/2016/bigrender/js/bigrender.js. Ask for star, ask for fork~

other

In addition to using textarea for BigRender optimization on the home page, Meituan also used script tags for optimization. For example, this product details page

High-traffic website performance optimization: step by step to create a suitable BigRender plug-in

Set a type other than "text/javascript" for the script tag, you can download this js, but don't execute it. This approach seems familiar, I have seen it in labjs.

For more information, please refer to the third continuation of front-end optimization: use script to store html code to reduce the number of DOM nodes


Read More

  • BigRender optimization of Taobao details page and the best way to store large chunks of HTML content

  • Front-end optimization: BigRender's textarea delayed rendering and the practice of LABjs

  • lazyload lazy loading components

  • KISSY lazy load data lazyload application

  • kissy datalazyload.js source code

  • kissy DataLazyload API

  • kissy DataLazyload demos

Guess you like

Origin blog.51cto.com/15080022/2588312