Web サイトがますます大きくなり、読み込み速度が遅くなると、開発者は Web サイトにアクセスするのに 10 秒も 20 秒も待ちたいと思うでしょうか。表示される Web ページについて?
一般的で比較的シンプルで簡単な最適化ソリューションは、画像の遅延読み込みです。巨大なページの場合、下のコンテンツを表示するためにスクロールしないことがあります。これにより、最初の画面以外の部分のレンダリングが無駄になります。このような無駄なレンダリングには、画像だけでなく、他の DOM 要素や一部の js/css も含まれます。一部の js/css はモジュールに基づいてリクエストされます (一部の ajax など)。理論的には、DOM が追加されるたびにレンダリング時間が増加します。 HTML、js、css をオンデマンドで読み込めるようにする方法はありますか?答えは「はい」です。これがこの記事の BigRender に関するものです。
Sina、Meituan、Tuniu Travel Network、360 Website Navigation、Taobao の商品詳細ページなど、業界の実稼働環境で BigRender が使用されているケースが多数あります。ソース コードを表示し (ctrl+u)、ctrl+f で textarea キーワードを検索すると、textarea タグで囲まれた HTML コードが簡単に確認できます。
たとえば、Tuniu:
そして、textarea タグでラップされたこれらの HTML コードは、単なる textarea の値であり、DOM ツリーにはレンダリングされません。そうです、BigRender は通常、textarea タグを値として含む HTML コード (js/css) をラップし、適切なタイミング (通常は textarea タグがユーザーの視野に表示されるか、表示されようとしているとき) を待って HTML を取り出します。テキストエリアのコードを innerHTML を使用して DOM ツリーに動的に挿入し、必要に応じて js/css コード (通常) を取り出して動的に実行します。 (画像の遅延読み込みに似ていますか?)
Yu おじさんは次のように指摘しました:
页面下载完毕后,要经过 Tokenization — Tree Construction — Rendering. 要让首屏尽快出来,得给浏览器减轻渲染首屏的工作量。可以从两方面入手: 1. 减少 DOM 节点数。节点数越少,意味着 Tokenization, Rendering 等操作耗费的时间越少。(对于典型的淘宝商品详情页,经测试发现,每增加一个 DOM 节点,会导致首屏渲染时间延迟约 0.5ms.) 2. 减少脚本执行时间。脚本执行和 UI Update 共享一个 thread, 脚本耗的时间越少,UI Update 就能越发提前。
なぜ textarea タグは大きな HTML コンテンツを保存するために使用されるのですか? Yu Bo の記事は今でも読むことができます。 Taobao の Kissy には DataLazyload コンポーネントが組み込まれています。 (中断: Meituan の詳細ページでも、BigRender の最適化にスクリプト タグが使用されています。詳細については、以下の「その他」セクションを参照してください)
次に、自分に合った BigRender プラグインを段階的に実装していきます。 HTML要素、js、cssの遅延読み込みができることを願っています。
jQueryの書き方を真似して、グローバルオブジェクトTを定義し、T.datalazyloadオブジェクトに遅延読み込みの実装コードをカプセル化し、「必要なコードをラップ」しました。遅延ロードされるようにします" "textarea タグで、その可視性属性を非表示に設定し、タグに「datalazyload」などの特別なクラス名 (イベント監視用) を付けます。便宜上、 各 bigrender に最適化された textarea の親ノードには子 (つまり、textarea 要素) が 1 つだけあることを規定します 。コードビハインドには特別な処理があるため、これは遵守することが非常に重要です。これ。 (親ノードの高さと幅は、レンダリング後に DOM の高さと幅と一致するように設定する必要があることに注意してください)
一部の HTML/js/css コードは、たとえば textarea タグで囲むことができます。 :
alert("I am lazyload zone!"); .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;} 我是延迟加载的部分!
ページの初期化時に、スクロールをリッスンする T.datalazyload オブジェクトの init() メソッドを定義します。 、モバイル側でのリサイズおよび 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 属性は、遅延ロードする必要があるテキストエリアのクラス名を示します。threshold は、しきい値 (ピクセル単位) を示します。テキストエリアのピクセル数をビューポートから取得します。
遅延ロードする必要がある要素を配列 (this.els) に保存すると、ロードが完了すると要素が配列から削除されます。イベントリスニングのコールバック関数は、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); }}
このメソッドの機能は、遅延ロードする必要がある要素がすでにビューポート内にあるかどうかを判断することです。その場合は、Load に進み (insert メソッドをトリガーし)、配列内の要素を削除します。配列が空の場合は、遅延ロードする必要があるすべての部分がロードされ、リスニングしているイベントが削除されたことを意味します。遅延読み込み全体が終了します。
insert メソッドを見てみましょう。 inert メソッドのパラメータは、遅延ロードする必要がある textarea 要素です。明らかに、解析する必要があるコードはすべて textarea.innerHTML にあります。 extractCode メソッドを使用して js/css コードを抽出し、js/css をフィルタリングして残りがすべて HTML コードになるようにし、それを DOM に挿入します (これは、前述した「各テキストエリアの親ノード」です) 「子が 1 つだけある」場合は、親ノードの innerHTML 操作を直接使用できます)。ロード効果がある場合は、通常、親ノードにロード クラスを追加して削除します。最後に、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("]*>([\S\s]*?)\s*>", "img"), "") .replace(new RegExp("]*>([\S\s]*?)\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
正規表現を使用して js および css タグ部分を抽出します。
extractCode: function(str, isStyle) { var cata = isStyle ? "style" : "script" , scriptFragment = "]*>([\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
正規表現の部分表現を巧みに使用して、スクリプト タグとスタイル タグ内のコンテンツを正常に抽出しました。
スクリプトの実行、スタイルのレンダリング。
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);}
简单讲讲 BigRender 优化的优缺点,以及适用场景。
优点很明显,因为减少了首屏 DOM 的渲染,所以能加快首屏加载的速度,并且能分块加载 js/css,非常适用于一些模块区分度很高的网站(个人觉得大型网站的模块区分度普遍越来越高了)。
缺点是需要更改 DOM 结构(DOM 节点的替换和渲染),可能会引起一些重排和重绘。一些没有开启 js 功能的用户将看不到延迟加载的内容(可以用 noscript 标签给出一个善意提醒)。最大的缺点可能是不利于 SEO,一些依赖于 SEO 的网站可能需要在 SEO 上下点功夫了,比如美团。
关于 SEO,可以看下 http://www.seoqx.com/lynx 这个网站,能模拟搜索引擎蜘蛛对网站的爬取情况。美团对于 BigRender 以及 SEO 解决方案 [美团网案例]改善BigRender技术导致的SEO问题
bigrender 通过减少 DOM 节点,加快首屏的渲染,但是,它也是有额外的性能损耗的,渲染前textarea 里面的 html 代码,在服务端把 html 代码保存在隐藏的 textarea 里面,所以在服务端会把 html 代码转义:尖括号等都被转义了,这个会增加服务器的压力;而且,这个改造只是前端的渲染,服务器依旧是一次计算所有的数据,输出所有的数据,这一点没有得到提高。
一般来说,使用都是后端拼接成 html 字符串,然后塞入 textarea 标签中,吐给前端。
如果要做一个完整的 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 标签做优化。比如 这个商品详情页
给 script 标签设置个非 “text/javascript” 的 type,可以下载这段 js,但不执行,这种做法似曾相识,在 labjs 中看到过。
更多可以参考 前端优化三续:用script存放html代码来减少DOM节点数
打赏支持作者写出更多好文章,谢谢!
打赏作者
a JavaScript beginner 个人主页 · 我的文章 · 4 ·