更新日:2017年03月07日

0. 環境

  • フロントエンドの話なのでサーバー環境はあまり関係ないのですが、参考まで。

    • サーバー

      [Nginx] 1.10.2
      [Kitura] 1.1.2
      [Swift] 3.0.1
      
    • ローカル

      [Mac] 10.11.6
      [Node.js] 7.6.0
      [gulp] 3.9.1
      

1.準備

  • 仕事でやっているWebサイトのスピードアップを個人でもやってみたくて、適当なページを1つ作ってみました。

    シンプルカレンダー

    そしてパフォーマンス改善の教科書に選んだのはこちらです。

    Speed Up Your Website

2.現状

  • Apacheで運用しているWebアプリが他にあるため、下記のようなちょっと珍しい構成になっています。

    Nginx(リバースプロキシとして使用) + ServerSide-Swift(Kitura)
    

    僕のネット環境(下り57Mbps/上り18Mbps)(※1)だとシンプルカレンダーのロードタイムが約2.5秒(※2)でそこまでストレスは感じませんが、DBを使ってないわりにこの数字なので改善の余地ありです。

    (※1) 回線速度はSPEEDTESTで計測。

    (※2) Chrome Developer Tools で計測。

3.ブラウザからのリクエスト削減

  • Chrome Developer ToolsのNetworkタブで見るとCSS、JavaScript(以下JS)のダウンロードに時間がかかっているので、教科書の[1. Reduce number of HTTP requests]-[Combine files]から取り掛かってみました。

    HTMLから静的ファイル(CSS、JSなど)を複数ダウンロードする際、ブラウザが並列でリクエストできる数が決まっているため、なるべくそれを減らしましょうという内容です。

    複数の静的ファイルをまとめるためのツール(※3)を検討しましたが、一番簡単に実現できそうだったgulpを使いました。

    (※3) webpack、gulp、Grunt

4.Node.jsの準備

  • Node.jsのバージョンを切り替えられるよう、nodebrew を使いたいと思います。(既にNode.jsをインストール済みの場合、読み飛ばしてもOKです)

    HomebrewでNode.jsをインストール済みの場合は一度アンインストール。

    $ brew uninstall node
    

    nodebrewをインストール。

    $ brew install nodebrew
    
  • nodebrewへのパスを通すため、.bashrcの末尾に下記を追記。

    $ vim ~/.bashrc
    
    # これを追記
    export PATH=$HOME/.nodebrew/current/bin:$PATH
    

    設定を反映します。

    $ source ~/.bashrc
    
  • Node.jsインストール。

    $ nodebrew install-binary latest
    

    エラーが出たので、このページに従ってディレクトリを作成。

    $ mkdir ~/.nodebrew
    $ mkdir ~/.nodebrew/src
    

    使用するNode.jsのバージョンを指定。

    $ nodebrew ls
    v7.6.0
    
    $ nodebrew use v7
    
    $ node -v
    v7.6.0
    

5.gulpの準備

  • gulpをインストール。

    自分だけのローカル環境なのでグローバル(-g)を指定してインストール。

    $ npm install -g gulp
    

    アプリ(プロジェクト)のルートディレクトリで下記を実行すると、package.jsonファイルが作成されます。(これにパッケージ情報が書き込まれる)

    $ npm init
    

    そのままgulpとgulpのプラグインをインストールし、”–save-dev”を付けることでpackage.jsonに記録します。

    $ npm install --save-dev gulp
    $ npm install --save-dev gulp-concat gulp-uglify gulp-minify-css
    

    アプリのルートディレクトリに gulpfile.js というファイルを作成し、こちらの内容を記入します。(パスは自分の環境に読み替えて下さい)

    続けて下記コマンドを打つと指定したディレクトリに結合・圧縮したCSS、JSファイルが出力されます。

    $ gulp
    

    HTMLからはそれらを読み込むよう修正します。(上記の例ではbundle.css、bundle.js)

    イメージはこんな感じです。

    gulpイメージ

  • ブラウザでJSエラー発生。

    修正後の画面をブラウザで確認したところ下記エラーが表示されました。

    Bootstrap's JavaScript requires jQuery
    

    どうやら上記の書き方だと、CSSやJSの読み込み順序が任意になり、BootstrapでjQueryが使えない状態になってるようです。

    もともとHTMLに書いてあったのと同じ順序でCSSとJSを指定するとエラーは出なくなりました。

    (IE9以下の指定で読み込んでいたJSもとりあえず含んでいます)

6.バックエンドとの接続不良

  • 本番へアップすると、数回に一回、ブラウザ側でエラーが発生しました。

    GET https://jyear.net/simple-cal/js/dist/bundle.js net::ERR_CONTENT_LENGTH_MISMATCH
    (index):47 GET https://jyear.net/simple-cal/css/dist/bundle.css net::ERR_CONTENT_LENGTH_MISMATCH
    (index):2196 Uncaught ReferenceError: $ is not defined
    

    このエラーが起きるとNginxでは下記のログが出力されていました。(【】はマスク)

    upstream prematurely closed connection while reading upstream, client: 【IP】, server: jyear.net, request: "GET /simple-cal/css/dist/bundle.css HTTP/1.1", upstream: "http://【ホスト名】/simple-cal/css/dist/bundle.css", host: "jyear.net", referrer: "https://jyear.net/cal/“
    

    いろいろネットで調べたところ、バックエンドから接続が切られるとこのエラーがでるようです。

    Nginxのproxy_connect_timeoutや、Kituraにタイムアウト設定があるか調べたのですが、解決策が見つからなかったため、1つにしていたbundleファイルを分割することにしました。

    分割して1ファイル当たりのサイズを抑えることでエラーは出なくなりました。

    (そもそも1ファイル約200KBだったのでWebで扱うには大きかった…)

    最終的な gulpfile.js はこちらです。

7.結果

  • ここまでやって前後の比較をしたところ、Chromeで下記の結果になりました。

    BeforeAfter
    Load
    (5回の平均)
    2.2秒1.6秒
    転送量518KB451KB
    リクエスト数2819

    また、他の計測ツールとしてGTmetrixを使おうとしたのですが、エラーになってしまったため、LighthouseというChrome Extensionを利用しました。

    Lighthouseによる計測結果の詳細はこちら。Before After

    “Page load performance”の改善でトータル・スコアも若干上がりました。(42→44)