Django/Mezzanine Templateにcritical CSS 組み込む思索と施作 1


PageSpeed Insights に、[スクロールせずに見えるコンテンツのレンダリングをブロックしている JavaScript/CSS を排除する] というチェック項目があります。
このチェック項目をクリアするには、CSS、js の非同期読み込みをするか、CSS、js の一部をインライン化して HTML 内に記載する必要がありますが、ベージのレイアウトごとにインライン化が必要な css、js は異なるでしょうし、対応するのがたいへんな印象でした。
しかし、調べてみると Critical CSS を生成するツールは結構あるようで、これを Django/Mezzanine のテンプレートに組み込めるか試してみました。

この記事では、critical CSS の作成方法と、css の遅延読み込みについて記載します。


参考


静的ページ、動的ページ に共通する 基本的な critical CSS の読み込み方

以下のような流れになります。
基本的に、critical CSS のインライン化、画面全体の CSS の非同期読み込みをセットで実施する必要があります。
1. critical CSS を 生成する。

  1. critical CSS を inline で HTML 上に記載する。

  2. 画面全体の CSS は javascript で、非同期で読み込む 1
    [1] HTML のサイズが14KB を超えると、あまり意味がないようで、試しに記事ページのバイト数を見ていたら、40KB くらいあって、HTML の組み方を変えると違うのかもしれませんが、 bootstrap 使っている限りは、14KB 以下するのは難しそうに思います。

2. は、Django/Mezzanine のテンプレート内で実施する処理になります。
一旦 2. は置いて 1.3. のcritical CSS の作成を行います。


Critical で critical CSS を作成する

Critical CSS の生成ツールとして、以下があります。2
[2] 全て npm ですね。最近何故かfrontend関連の調べものをすることが多いですが、ほとんどというか全てツールが npm でこの辺りが疎くて、すごい遅れをとっている感を味わっております。

現状(2017/09/24) で、メンテもされている、且つ、コマンドラインからの実行もできる critical を使ってみます。


Docker image を作り、CLI を実行する

critical を 実行するための docker image がないか探してみたのですが、見つからなかったので docker image を作成しました。
kemsakurai/docker-critical - Docker Hub

  • critical CSS を出力する
    input に、対象ページURLを指定すると対象ページのcritical CSSが出力されます。
    --minify をオプションで critical CSSが圧縮されます。

    docker run -v "$(pwd)":/critical_result -it kemsakurai/docker-critical \
    /bin/sh -c 'critical https://www.monotalk.xyz --base ./ --minify > /critical_result/critical.css'
    

  • critical CSS を含むHTMLを出力する

    docker run -v "$(pwd)":/critical_result -it kemsakurai/docker-critical \
    /bin/sh -c 'critical https://www.monotalk.xyz --base ./ --minify --inline > /critical_result/critical.html'
    

  • 出力ファイル 上記コマンド実行すると、カレントディレクトリに、css、htmlが出力されます。

    ls
    critical.css    critical.html
    


Docker image 上で、gulp タスクとして実行する

critical の gulp タスクを実行します。
blog の topページ、記事ページ、aboutページのcritical CSS を作成するため、 以下のような gulpfile を作成しました。

  • gulpfile.js

    var gulp = require('gulp');
    var critical = require('critical');
    
    gulp.task('critical_blog_list', function() {
        return critical.generate({
            src: 'https://www.monotalk.xyz',
            dest: '/critical_result/critical_blog_list.css',
            minify: true
        });
    });
    
    gulp.task('critical_blog_detail', function() {
        return critical.generate({
            src: 'https://www.monotalk.xyz/blog/wicket-browser-%E3%81%AE%E6%83%85%E5%A0%B1%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B/',
            dest: '/critical_result/critical_blog_detail.css',
            minify: true
        });
    });
    
    gulp.task('critical_about', function() {
        return critical.generate({
            src: 'https://www.monotalk.xyz/about',
            dest: '/critical_result/critical_about.css',
            minify: true
        });
    });
    
    gulp.task('default',['critical_blog_list','critical_blog_detail','critical_about']);
    

  • コマンド 無理矢理感が強いですが、以下コマンドで、localで作成したgulpfile.jsをカレントディレクトリに配置して、
    docker 上の gulp install ディレクトリにコピーして、gulp コマンドを叩きます。

    docker run -v "$(pwd)":/critical_result -it kemsakurai/docker-critical \
    /bin/sh -c 'cp /critical_result/gulpfile.js ./ && gulp'
    

  • OUTPUT
    各ページのcritical CSSが出力されました。

    % ls
    ------------------------------
    critical_about.css      critical_blog_detail.css    critical_blog_list.css      gulpfile.js
    

cli でも gulpfile.js でも critical CSS の生成が可能でした。
Django から呼び出すことを考えると、cli を使うのが扱い易そうです。


css の非同期読み込みのために、loadCSS を使う

critical CSS の生成は上手くいきましたので、既存の css の遅延ロードを行います。
link タグでの非同期読み込みはブラウザの互換性とか気にしないといけない部分が多そうだったので、locadCSS というライブラリを使うことにしました。
filamentgroup/loadCSS: A function for loading CSS asynchronously
Github 上には、min.js がなかったため、通常のjsをダウンロード後にJavaScript Minifier を使用して圧縮しました。3
[3] 圧縮したものをGist に上げている方もいるようです。
loadCSS.min.js

yepnope.js というライブラリも同様の用途に使えそうで、locadCSS よりも多機能なようです。
ページの初期描画を高速化させる、yepnope.jsの使い方 - ふろしき.js
現状、css の遅延読み込みができればOKなので、loadCSS を使う方針とします。


Django-compressor と loadCSS の兼ね合い

このブログでは、Django compressor を使用しています。
Django compressor はテンプレートが描画される際に、動的に css の 圧縮、結合を行い、5ebef948ce2c.css のようなランダムな 名前 のついた css を生成します。 この名前を取得してloadCSS で指定する必要がありますが、以下、loadCSS に纏わる Issue が作成されており、現状は機能はないようですが、compressor の subclass を作ることで、対応は可能な旨が記載されていました。


まとめ

critical CSS の 生成方法と、遅延読み込みについて記載しました。生成とロード自体は問題なくできそうです。
Django の template tag と、command を上手く実装できれば、実現できそうに思います。
以上です。

コメント