Django/Mezzanine Templateにcritical CSS 組み込む思索と施作 1 | Monotalk続きです。
critical CSS を Django/Mezzanine の template に組み込む方法を検討します。


前提

以下の環境で実行しています。

  • OS
    CentOS release 6.9 (Final)

  • Python Version
    Python 2.7.8

  • Package (必要そうなものだけ抜粋)
    Django (1.10.7) Mezzanine (4.2.3) mezzanine-pagedown (1.0)


動的ページ での critical CSS を読み込む際の関心事

critical CSS は、「卵か先か、鶏が先か」な代物で、実装パターンとしては以下、3パターンがあると考えました。

  1. ページ描画時に critical css を生成する。次回の読み込み時は cache から取得する。2
    [2] 一度目の読み込み時は遅い。

  2. デプロイ後に、別タスクで、critical css を生成する。ページ側はファイルが生成されていたら、html 上にインライン展開する。

  3. 開発環境でデプロイ後に、事前に生成しておく。Production 環境では事前生成したものを組み込み、読み込む。

3 でできると、パフォーマンス、耐障害性は良くなりそうですが、個人 Blog の投稿、デプロイの仕組みとしては組み込みが難しいため、1、2 のどちらかで上手く実装できるかを検討します。


Django pulgin で 作成されているものがあるか ?

Django で、critical CSS の生成、組み込みが可能が plugin があるか調べてみました。
現在、martinblech/django-critical: Inlines critical path CSS and defers loading full CSS asynchronously.見つかりました。
この記事では、このプラグインをインストールして、具合を見てみようかと思います。
以降は、django-criticalついて記載します。


Django-critical を使う

設定の流れ

django-critical使用するためには以下が必要になります。

1.前提となる node.jsライブラリのインストール
django-critical は、pocketjoso/penthouse: Generate critical css for your urls依存していて、 penthouse npm依存していて、npm node.js依存しているため、周辺のミドルウェアのインストールが必要になります。

2.django-critical のインストール、設定
django-criticalインストール、設定を実施します。

3.template ファイルの調整
template ファイルに読み込み記述を追加する必要があります。

4.動作確認

1. 前提となる node.jsライブラリのインストール

node.js 関連のツールをインストールしていきます。

  • node npm のインストール
    node.jsをyumでインストールする(centos6.5) - Qiita参考にしました。

    yum install gcc gcc-c++
    yum install epel-release
    yum install nodejs npm --enablerepo=epel
    
    WARNING が出ますが、強行します。
    パッケージをダウンロードしています:
    warning: rpmts_HdrFromFdno: Header V4 RSA/SHA1 Signature, key ID f2ee9d55: NOKEY
    Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-SCLo
    Importing GPG key 0xF2EE9D55:
     Userid : CentOS SoftwareCollections SIG (https://wiki.centos.org/SpecialInterestGroup/SCLo) <security@centos.org>
     Package: centos-release-scl-rh-2-3.el6.centos.noarch (@extras)
     From   : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-SIG-SCLo
    

  • penthouse のインストール
    django-critical使用可能な penthouse version は 結構前のものです。
    python からは、index.js ではなく、penthouse.js指定する必要があり、0.4.0 以前だと、penthouse.jsバンドルされていますので、ここでは、alpha 版ではない1番新しい version の0.3.6インストールします。
    また、settings.py default 設定に従う場合は、django-project の root フォルダの配下に node_module フォルダがあると、設定がしやすいので、install コマンドは django-project の root フォルダで実施するのをお勧めします。

    npm install penthouse@0.3.6       
    

2.django-criticalのインストール、設定

django-critical には、encoding 関連のバグがあり、マルチバイド文字列を含む html 、もしくは、encoding 設定が UTF-8 等だと、plugin 内の処理でエラーとなります。このため、以下 pullrequest を参考に、プログラムを修正する必要があります。
あまりよい修正の仕方ではないのかもしれませんが、取り込めば動かすことができます。
Fixes Encoding issue by programmdesign · Pull Request #4 · martinblech/django-critical

django-critical を folk して上記の修正を取り込んだものを以下に UP しました。 kemsakurai/django-critical: Inlines critical path CSS and defers loading full CSS asynchronously.
この folk したものをインストールします。

django-critical のインストール

pip で django-criticalインストールします。

pip install git+https://github.com/kemsakurai/django-critical
pip list | grep django-critical
---------------------------------------------
django-critical (0.1.1)
---------------------------------------------

設定

基本的に、README.md の設定に従い設定を行います。
settings.py設定を追加します。

  • INSTALLED_APPS critical を追加

    INSTALLED_APPS = (
        # other apps
        "critical",
    )
    

  • MIDDLEWARE_CLASSES に、"critical.middleware.CriticalCssMiddleware"追加

    MIDDLEWARE_CLASSES = (
        ....
        # django-htmlmin
        "htmlmin.middleware.HtmlMinifyMiddleware",
        "critical.middleware.CriticalCssMiddleware",
        "django.contrib.sessions.middleware.SessionMiddleware",
        ...
        )
    

  • CRITICAL_PENTHOUSE_PATH と、CRITICAL_PHANTOMJS_PATH の設定
    以下のように、penthouse.js path と phantomjs path を設定します。
    phantomjs は、penthouse.jsインストールすると、芋づるでインストールされます。

    CRITICAL_PENTHOUSE_PATH = os.path.join(
        BASE_DIR, 'node_modules/penthouse/penthouse.js')
    
    CRITICAL_PHANTOMJS_PATH = os.path.join(
        BASE_DIR, 'node_modules/penthouse/node_modules/phantomjs/bin/phantomjs')
    

3.template ファイルの調整

以下、テンプレートタグの抜粋です。全量はmezzanine-theme-clean-blog/base.html at master · kemsakurai/mezzanine-theme-clean-blogご確認ください。

  • base.htmlの抜粋

        {% load critical %}
        {% critical %}
        {% compress css %}
            <!-- Bootstrap Core CSS -->
            <link href="{% static "css/bootstrap.min.css" %}" rel="stylesheet">
            <link href="{% static "css/clean-blog.min.css" %}" rel="stylesheet">
            <!-- Custom Fonts -->
            <link href="{% static "css/font-awesome.min.css" %}" rel="stylesheet">  
            <link href="{% static "css/fonts.css" %}" rel="stylesheet">
            <!-- Code Hilite -->
            {% ifinstalled mezzanine_pagedown %}
            <link rel="stylesheet" href="{% static "css/codehilite.css" %}">
            {% endifinstalled %}        
            {% if LANGUAGE_BIDI %}
             <link rel="stylesheet" href="{% static "css/rtl.css" %}">
            {% endif %}
            {% ifinstalled cartridge.shop %}
            <link rel="stylesheet" href="{% static "css/cartridge.css" %}">
            {% if LANGUAGE_BIDI %}
            <link rel="stylesheet" href="{% static "css/cartridge.rtl.css" %}">
            {% endif %}
            {% endifinstalled %}
            {% block extra_css %}{% endblock %}
        {% endcompress %}
        {% endcritical %}
        .................
    
        {% autoescape off %}
        {% critical_async %}
        {% endautoescape %}
        </body>
    

  • criticalタグのロード
    {% load critical %}タグの読み込みをします。

  • criticalCSS を生成したい箇所を囲む
    {% critical %} {% endcritical %} で、criticalCSSを生成したい箇所を囲みます。
    上記は、タグ内に django-compressor の {% compress css %} タグがあり、圧縮、結合して作成されたcssファイルのcriticalCSSを生成します。

  • 元々のcss の遅延ロードを行う
    body タグ直前に以下のタグを追加することで、criticalCSSの出力対象 css ファイルを遅延読み込みすることができます。

        {% autoescape off %}
        {% critical_async %}
        {% endautoescape %}
    
    上記タグで、以下のHTMLが出力されます。
    <script>
    !function(e){"use strict";var n=function(n,t,o){function r(e){if(a.body)return e();setTimeout(function(){r(e)})}function i(){l.addEventListener&&l.removeEventListener("load",i),l.media=o||"all"}var d,a=e.document,l=a.createElement("link");if(t)d=t;else{var s=(a.body||a.getElementsByTagName("head")[0]).childNodes;d=s[s.length-1]}var f=a.styleSheets;l.rel="stylesheet",l.href=n,l.media="only x",r(function(){d.parentNode.insertBefore(l,t?d:d.nextSibling)});var c=function(e){for(var n=l.href,t=f.length;t--;)if(f[t].href===n)return e();setTimeout(function(){c(e)})};return l.addEventListener&&l.addEventListener("load",i),l.onloadcssdefined=c,c(i),l};"undefined"!=typeof exports?exports.loadCSS=n:e.loadCSS=n;
      var elem = document.getElementById("django-critical-async-css-marker");
          loadCSS('https://www.monotalk.xyz/static/CACHE/css/690a0d57c104.css', elem);
      elem.parentNode.removeChild(elem);
    }("undefined"!=typeof global?global:this);
    </script>
    
    で、もともと CSS のリンクが出るはずの場所に、inline CSS が書き出させれます。

4.動作確認

動作確認のした際の挙動について記載します。

  • 初回表示時は異常に遅い
    初回表示時、ページ描画とともに、penthouseコマンドが実行され、phantomjs起動します。
    10秒ほどページ描画に時間がかかり、inline CSS の cache が作成され、2回目の表示以降 cahce が使われ早くなる動作となります。
    ですので、実ユーザが初回起動にあたらないように、cache 作成のタイミングを工夫する必要があるかと思います。

  • 実施前、実施後の Page Speed Insight の点数
    実施前、88点で、実施後、92点になりました。
    実施労力の割に点数の上がり幅は低いです。

まとめ

Django/Mezzanine の template に critical CSS を組み込むため、django-critical使ってみました。
以下、まとめます。

  • django-compressor との相性がいい。
    compressor で 圧縮/結合した CSS を入力に用いることができ、また CSS の遅延読み込みが可能なため競合することなく使えます。

  • メンテされていないが、動く。
    日本語環境で使えないバグもあり、依存する node ライブラリが古いです。頑張って動かすことはできましたが、仕事で使ったりするには少し不安です。

  • エンコードの問題を解決し、新しめの critical で動かす?
    penthouse新しい version で動くようにして、エンコードの問題を解決すれば、結構イケてるものになりそうな気がします。

critical を試してみたので、それで動作するように改造してみようかと思います。
以上です。

コメント