$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

「百万畳ラビリンス」読んだ

最近マンガ大賞2022というのを見つけて、それにランクインしている「百万畳ラビリンス」を読んだ。最後まで一気に読めて面白かった。

百万畳ラビリンスは主人公がひたすら和室が続く変なところに突然閉じ込められるというところからストーリーが始まる。その中にいろいろな謎があり、それが少しずつ解き明かされていくところが非常に良かった。謎というのもミステリー的な推理するための謎というより、このアイテムは実はこう使えるみたいなゲーム的な謎が散りばめられている感じで、漫画を読んでいるとRPGをプレイしているような気分になれて、漫画を読んでいて初めての体験だなあと思った。個人的には最後が少し消化不良だったが、それでも面白かった。

2巻しかないしすぐ読めるのでおすすめ。

フロントエンド速度改善でやったこと(Expiresヘッダ、faviconのgzip圧縮、JSの読み込み遅延化)

フロントエンド速度改善をしようとして参考にしたもの - $shibayu36->blog; という記事を以前に書いたのだけど、結局何をやったか書いて欲しいと社内で言われたので、今回のフロントエンドの速度改善でやったことについて書いてみる。そこまで大したことはやってないので参考程度にどうぞ。

前提

ページのレンダリングが遅いと思い始めたので改善をすることになったのだが、改善をし始めたところChromeのアップデートがあり爆速になってしまった(FirefoxSafari等はもともと速かった)ので、では明らかにやったほうが良いことだけやりますかという話になった。そのためあんまりbefore/afterもちゃんと取っていないので、今回はやったことの紹介くらいに留める。

やったこと

計測や調査をしてみたところ、以下のようなことはやってしまったほうが良いということになった。

  • 静的ファイルに適切にExpiresを付ける
    • 理由): 改善前はLast-ModifiedやETagしか付いていなかったため、304は返るもののリクエストが飛んでしまったため。そもそもリクエストを飛ばないようにしたい
  • faviconやWebフォントのもgzipで転送する
    • 理由): 基本的にgzip圧縮をかけていたのだが、faviconとWebフォントは漏れていて、特にfaviconの転送量が意外と大きかったため
  • JSの読み込みをbodyの一番下に or async属性を付ける
    • 理由): JS読み込み完了までレンダリングがブロックされるため、読み込みを遅らせたい

静的ファイルに適切にExpiresを付ける

改善前の状態

今運用しているサイトでは、画像やCSS、JSなどの静的ファイルのURLには必ずファイルの中身のdigest値がクエリに付くようにしている。例えば、 /css/app.css?p45XAdRzAntl のような感じ。

ファイルの中身のdigest値がURLに入るということは、内容が変われば必ずURLが異なるということである。内容が変わった時は必ずURLは変わるので、配信時にはExpiresを遠い未来に設定しても、ファイル変更時に古いキャッシュを見てしまうということはない。なので、全ての静的ファイルに適当に遠い未来のExpiresを設定しておけば良い。

しかし、改善前は設定を忘れていて、Last-ModifiedとETagのみ返してしまっていて、Expiresがつかない状態になっていた。

改善案

作戦としては、以下のようにした。

  • 静的ファイルにはExpiresを無条件につけたら良い。期間はある程度長ければ適当で良いが、ひとまず30日くらいとする。
  • Last-ModifiedやETagに関しては、プロキシサーバごとにずれる問題が面倒だし、Expiresが正しく付けば不要と判断。それぞれ消しておくことにする。

プロキシとしてnginxを利用していたので、以下の様な設定を入れた。

location ~ ^/(css|js|images|font)/ {
    # expiresは30日
    expires 30d;

    # etagはdefaultでonなのでoffに
    etag off;

    # if_modified_sinceは無視し、Last-Modifiedも消しておく
    if_modified_since  off;
    add_header Last-Modified "";

    root /path/to/static;
}

参考

補足

静的ファイルのURLにdigest値を入れるのは、テンプレートエンジンでURLを生成する時にメソッドを通す & css内のURLはgulpでビルド時に置き換える、ということをやっているのだけど、これに関してはまた機会があったら方法について書こうと思う。

faviconやWebフォントのもgzipで転送する

改善前の状態

基本的にはコンテンツはgzipで転送すればよい(ただし既に圧縮している画像ファイル等は除く)、と思っていたので、基本的なgzipの転送設定はされていた。しかし、その中でfaviconやWebフォントのgzip転送の設定が漏れていた。

特にfaviconに関しては ホームページ制作なら渋谷のウェブ制作会社【スタイル】 のように、最近はマルチサイズで保存していることも多く、容量が意外と大きくなったりする。今回の場合もfaviconが数百KBで転送されており、上記のExpiresによってクライアントにキャッシュされるとはいえ、容量が大きいということになった。

改善案

実際にfaviconのファイルをgzip圧縮をかけてみると、数百KBのファイルが数KB程度まで圧縮できた。またgoogleのfavicongzipで圧縮されて転送されており、設定も簡単なのでfaviconとWebフォントも普通にgzip圧縮をかけて転送しても問題なかろうと判断した。

そこでhttp://blog.hoerin.com/2014/11/11/nginx%E3%81%A7gzip%E5%9C%A7%E7%B8%AE%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B/ のようにgzip圧縮の設定をした上で(正確にはサーバに合わせたgzip_buffers等の設定を適切に行った上で)、gzip_typesにfaviconやWebフォントを追加した。

例えばこんな感じ。よくある設定にapplication/font-sfntやapplication/font-woff、image/x-iconを追加している。

gzip_types text/css application/x-javascript text/javascript application/atom+xml application/rss+xml application/xml application/json application/font-sfnt application/font-woff image/x-icon

参考

JSの読み込みをbodyの一番下に、もしくはasync属性を付ける

改善前の状態

普通にheadタグ内でJSを同期的に読み込んでいた。読み込み完了するまでレンダリングが停止するので、まあ普通にbodyの一番下に持ってくるか、PageSpeed Insightsに指摘される通り、async属性をつけたほうが良いよねということになった。

改善案

とりあえず一番下に持ってきたらいいよねということになった。しかし一つだけ問題があって、読み込むスクリプト(例えばjQuery)に依存するちょっとしたコードを、scriptタグでインラインで書いている部分があり、単に一番下に持ってきたらエラーが出るようになってしまった。

こんな感じ

<head>
  <script src="/js/app.js"></script>
</head>
<body>
  <script>
    $(document).ready(function () {
       ...
    });
  </script>
</body>
  • インラインのJSが、外部から読み込んでいるJSに依存していることがあんまり良くない
  • そんなに大したコードは書かれていない

という理由から、jQueryのコードを標準APIのコードに書き換えた。

書き換えのためには以下のような記事やサイトを参考にした。

書き換えが終わった後は普通にbodyの一番下に読み込みを移動。

<body>
  ...
  <script src="/js/app.js"></script>
</body>

google推奨はasyncなので、そちらにしたほうが良いかもしれないが、あまり検証はしていない。

まとめ

フロントエンド速度改善をしようとして参考にしたもの - $shibayu36->blog; の続きで、実際にやったことについてまとめてみた。本当はちゃんとbefore/afterを計測したほうが良いのだけど、ユーザーが感じる体感がそこまで悪く無さそう & 明らかにやっても良いことはやってしまおうということになったので、ちょっと雑ではあるけど、参考になればと思う。

Google Tag Managerを使ってバナーやメッセージをデプロイなしに切り替える

サイトを運用していると、例えばトップページのサイドバーにバナー画像を出したくなったり、ユーザーへの告知用のメッセージをトップページに出したいなどということがあります。この時、自由にその場所のHTMLを書き換えたいけど、書き換えるたびにコードをデプロイしないといけないと大変です。そこで、今回はGoogle Tag Managerを使ってバナーやメッセージを配信するのを試してみたので紹介します。

どうやってやるのか

Google Tag ManagerはAnalyticsの設定を入れたり、任意のHTMLを入れたりなど、Googleの管理ツールを触るだけで簡単にサイトにタグを入れたりできるツールです。そしてGoogle Tag ManagerのカスタムHTMLというのを利用することで、自分の好きなHTMLタグをサイトに差し込むこともできます。

これを利用して、カスタムHTMLにバナーを入れるためのscriptタグを入れることで、Googleの管理ツールを少しいじるだけで、サイト内部のHTMLを書き換えることが出来るようにしてみようと思います。

今回はGoogle Tag Managerの初期設定は終わっている前提で紹介します。

トリガーを設定する

まずバナーの差し込みを発生させるためのトリガーを設定します。

f:id:shiba_yu36:20160325232114p:plain

この左のサイドバーからトリガーをクリックして、その後「新規」というボタンを押します。トリガーの設定は以下のようにします。

  • イベントは「ページビュー」を設定
  • トリガーのタイプは「DOM Ready」
  • 配信するタイミングには「一部のページビュー」を選択し、「url pathを/に等しい」にします

f:id:shiba_yu36:20160325232459p:plain

これで、「トップページを開いてDOMが読み込み完了したら発火する」というトリガーを設定することができました。ちなみにこのトリガーは結構柔軟で、URLが正規表現にマッチした時とか、カスタムイベントが発火した時とか、クリック時に発火してそのエレメントのIDが◯◯であった時、とか結構何でも設定できそうでした。https://support.google.com/tagmanager/answer/6106961?hl=ja を参考にしましょう。

続いてこのトリガーが発火した時に差し込むタグを設定します。

タグを設定する

続いて左のサイドバーからタグをクリックし、「新規」ボタンを押します。そして、「カスタムHTMLタグ」をクリックします。

f:id:shiba_yu36:20160325233225p:plain


タグを設定のところには以下のようなscriptを書きます。このスクリプトでやっていることは単純で、js-gtm-banner-containerクラスを持っている要素の中に、差し込みたい任意のHTMLタグを挿入しているだけです。これにより、サイトにこのクラスを持った要素を置いておけば任意のHTMLを差し込めるようになります。

<script>
  var container = document.querySelector('.js-gtm-banner-container');
  container.innerHTML = '<a href="http://blog.shibayu36.org/">shibayu36のブログです</a>';
</script>

配信するタイミングでは、その他を選択し、先ほど作ったトリガーを選択します。
f:id:shiba_yu36:20160325233439p:plain

ここまで来たらGoogle Tag Managerで変更を公開しておいてください。

これで設定はひとまず完了です。今の段階で、「トップページを開いたらその中にあるjs-gtm-banner-containerクラスを持つ要素の中に任意のHTMLを挿入する」という設定をGoogle Tag Managerを使って設定することができました。

サイトの差し込みたい場所に要素を置いておく

ここまで設定できたら、サイトのトップページのバナーやメッセージを差し込みたい場所に以下の様なHTMLを置いておくだけです。

<div class="js-gtm-banner-container"></div>

これを書いた状態で一度だけデプロイしておけば、あとはGoogle Tag Managerのcontainer.innerHTMLに代入している部分を書き換えるだけでサイトのデプロイなしに該当箇所のHTMLを変更することが出来るようになりました。HTMLをいろいろ書けるので、画像を置いたりリンクを置いたりとなんでもできますね。

まとめ

今回はサイトのデプロイなしにメッセージやバナーを入れ替える方法についてまとめてみました。Google Tag Managerはいろいろできるので便利ですね。紹介した例ではトップページが表示された時にバナーを出すようにしていましたが、Google Tag Managerの設定によっては特定のクラスを持つタグがあった時に、その内部を差し替えるみたいなことも普通にできそうな感じでした。