日々のコンピュータ情報の集積と整理

Dr.ウーパのコンピュータ備忘録

2015年2月23日月曜日

Blogger:JavaScript:大量の投稿があるブログでも、ページ表示時の負荷を抑えて投稿一覧(目次)を作成

改良後のBloggerの全投稿一覧(目次)
改良後のBloggerの全投稿一覧(目次)

イントロダクション

このブログでは、今までに公開した全投稿のリストを、以下のページで公開しています。

Dr.ウーパのコンピュータ備忘録: 投稿一覧
http://upa-pc.blogspot.com/p/posts.html


この全投稿のリストは、JavaScript によって動的に作成しています。
今までは、以下のページの JavaScript コードを使用して、全投稿の一覧を作成していました。

Blogger:投稿の数が500件を超えていても、意識しないで投稿一覧を作成出来るようにしました - Dr.ウーパのコンピュータ備忘録
http://upa-pc.blogspot.jp/2014/05/blogger-posts-list-8.html


しかし、投稿の数が何百件(現時点で 617 件)にもなってくると、全投稿の一覧を表示するときに物凄く負荷がかかるようになってきました。


私のメインデスクトップPCでは、あまり処理の負荷は気にならなかったのですが、普段メインで使用しているメインノートPCでは、全投稿のページが表示されてから、実際に操作可能になるまで十数秒程度、Chrome の画面が固まるような現象が発生していました。

メインデスクトップPC{Core(TM) i7-2600K プロセッサー (クアッドコア/3.40GHz/TB時最大3.80GHz/8MB スマートキャッシュ/HT対応), 8GB メモリ [ 4GB×2 (DDR3 SDRAM PC3-10600) / デュアルチャネル]}には劣ると言っても、メインノートPCは、Core 2 Duo P8600 (2.40 GHz)、メモリー 4GB(DDR2 800/2GBx2)のまだまだ使えるスペックのパソコンです。

そのスペックのPCで、ページ表示時に十数秒操作できない状態が続くというのは、流石に改良が必要だと判断しました。

そこで、大量の投稿があるブログでも、ページ表示時の負荷を抑えて全投稿の一覧(目次)を作成できるように、JavaScript コードを改良しました。

大量の投稿があるブログでも、ページ表示時の負荷を抑えて投稿一覧(目次)を作成


改良後の JavaScript コードは次のようになります。

改良後の JavaScript コード:

<div id="post-list-container">
</div>
<div id="post-list-gen-state">
記事一覧生成中...
</div>
<script type="text/javascript">
<!--
    // --- Util ---

    // JavaScript動的挿入
    function addScript(src) {

        var script = document.createElement('script');
        script.setAttribute("type", "text/javascript");
        script.setAttribute("src", src);

        header_setChild(script);
    }

    // <head>取得
    function getHeader() {
        return document.getElementsByTagName("head")[0];
    }

    // <head>に子要素を追加
    function header_setChild(child) {
        var head = getHeader();
        head.appendChild(child);
    }


    // --- Main ---

    var feedDataArray = [];         // フィードのデータ
    var max_results = 500;          // フィード取得件数(最大500)
    var blogger_url = location.protocol + "//" + location.host;

    

    // 記事一覧作成のトリガー
    (function () {

        loadFeed();
        
    })();

    // Feed の読み込み
    function loadFeed() {
        addScript(blogger_url + "/feeds/posts/summary?alt=json-in-script&callback=loadtoc&max-results=" + max_results + "&start-index=" + (max_results * feedDataArray.length + 1) + "&redirect=false");
    }

    function loadtoc(data) {

        // エントリーがある場合は、エントリーをストックし、次の記事の読み込み処理を実行
        if (data.feed.entry) {
            if (data.feed.entry.length > 0) {

                feedDataArray.push(data);

                // フィードの上限件数取得できていた場合、まだ投稿が残っている可能性があるので次のフィードを取得する
                if (data.feed.entry.length == max_results) {

                    // 次の feed の読み込み
                    loadFeed();
                    return;
                }
            }
        }

        // エントリーが無い・最後のエントリーまで読み込んだ場合
        // 全てのデータを読み込み終わったので、記事一覧を作成
        createPostsIndex();
    }

    function createPostsIndex() {

        var item_count = 0;         // 全アイテム数

        // フィードの生成に使うカウンタは、
        // 1 回の生成分を呼び出すときには持ち越しとする
        var feedCount = 0;
        var i = 0;
        

        // 1 回の生成分
        createPostsIndexPart();
        function createPostsIndexPart() {

            var item_part_count = 0;
            var item_part_max_count = 10;           // 一回の生成で作成する記事の数
            var item_part_wait_time = 200;          // 一回の生成が終わった後に、次の生成を開始するまでの時間(ms)

            var finish = false;                     // すべての目次項目の生成が終了したかどうか

            var obj_ol = document.createElement("ol");
            obj_ol.setAttribute("start", item_count + 1);

            var html = "";
            (function () {
                for (; feedCount < feedDataArray.length; feedCount++) {

                    var data = feedDataArray[feedCount];        // 処理対象のフィードの取り出し
                    for (; i < data.feed.entry.length; i++, item_part_count++, item_count++) {

                        // 1 回に生成すべきアイテムの数に達したら、今回の生成は終了する
                        if (item_part_count >= item_part_max_count) {
                            return;
                        }

                        // リンク先の探索
                        var href = "javascript:void(0);";
                        for (var j = 0; j < data.feed.entry[i].link.length; j++) {
                            if (data.feed.entry[i].link[j].title == data.feed.entry[i].title.$t) {
                                href = data.feed.entry[i].link[j].href;
                            }
                        }

                        // ラベル一覧作成
                        var labels = "";
                        if (data.feed.entry[i].category) {
                            for (var j = 0; j < data.feed.entry[i].category.length; j++) {
                                var label = data.feed.entry[i].category[j].term;
                                var labelLink = blogger_url + "/search/label/" + encodeURIComponent(label);

                                labels += "<span><a href=\"" + labelLink + "\">" + label + "</a><span>";
                                if (j < data.feed.entry[i].category.length - 1) {
                                    labels += ",";
                                }
                            }
                        }

                        html += "<li><a href=\"" + href + "\" title=\"" +
                            fixForAttributeTitle(escapeForAttributeText(data.feed.entry[i].summary.$t)) + "\">" +
                            escapeHTML(data.feed.entry[i].title.$t) + "</a><br />ラベル:" + labels + "<br /><br /></li>";


                        // 記事のまとまりごとにラインを引く
                        if ((i % 10) == 9) {
                            html += "<hr />";
                        }
                    }
                    i = 0;
                }

                finish = true;
            })();


            obj_ol.innerHTML = html;

            var obj_output = document.getElementById("post-list-container");
            obj_output.appendChild(obj_ol);

            if (finish) {
                document.getElementById("post-list-gen-state").innerHTML = "目次の生成が完了しました。";
            } else {
                setTimeout(createPostsIndexPart, item_part_wait_time);
            }
            

            // テキストをエスケープ処理する
            function escapeHTML(html) {

                var div = document.createElement("div");
                if (div.innerText !== void 0) div.innerText = html;          // innerText が定義されていれば innerText へ設定
                else div.textContent = html;                                 // Firefox のように innerText がないブラウザ向け

                return div.innerHTML;
            }

            // 属性用にテキストをエスケープ処理する
            function escapeForAttributeText(html) {

                // " をエスケープ
                html = html.replace(/\&/g, "&amp;");

                html = html.replace(/\</g, "&lt;");
                html = html.replace(/\>/g, "&gt;");
                html = html.replace(/\"/g, "&quot;");
                html = html.replace(/\'/g, "&apos;");
                return html;
            }

            // 属性の title 用にテキストを整理する
            function fixForAttributeTitle(text) {

                text = text.replace(/(\r\n){2,}/g, "$1$1");
                text = text.replace(/(\r){2,}/g, "$1$1");
                text = text.replace(/(\n){2,}/g, "$1$1");

                return text;
            }
        }
    }

//-->
</script>

このコードを、Blogger のページなどの HTML に張り付けると、大量の投稿があるブログでも、ページ表示時の負荷を抑えて投稿一覧(目次)を作成することができます。


改良ポイント

ページ表示時の負荷を軽減

改良のポイントとしては、ページ表示時に全投稿を表示するのではなく、ページの投稿をitem_part_max_count分(デフォルトで10件)ずつ小出しに表示していくようにしました。

小出しに投稿を表示する間隔は、item_part_wait_time ミリ秒(デフォルトで200)です。


以前の全投稿一覧生成 JavaScript コードのときに、Chrome が十数秒固まるという動作を見た時に感じたのは、投稿の一覧が作成し終わり、画面に表示された後に固まる現象が起きているということです。

従って、全投稿一覧作成処理が重いのではなく、一度に大量のデータを画面に表示すると、Chrome が十数秒固まるような問題が発生するのだと考えました。


そこで、タイマを用いて、時間間隔をあけて投稿を小出しにすることで、一度に画面に追加で表示するデータ量を抑え、Chrome が固まるような現象を発生させないことに成功しました。


その他修正ポイント

Blogger ブログの url に応じて、JavaScript コードを書き換える必要なし

前回の JavaScript コードでは、全投稿のデータを取得する Blogger ブログの URL を JavaScript コードに埋め込んでいたため、コード貼り付け時に各自でその部分を自分自身のブログの URL に書き換える必要がありました。

今回の修正にて、JavaScript コードを実行しているブログの URL を動的に取得するようにしたため、そのような URL の書き換え作業が不要になりました。









関連記事

関連記事を読み込み中...

同じラベルの記事を読み込み中...