読者です 読者をやめる 読者になる 読者になる

Master PG

プログラムし続ける...

【Polymer Starter Kit】BrowsersyncでWEBサーバー環境を整える


目次


概要

これから数回に渡ってPolymerを使用したプロジェクトで実際に開発をしていくための「環境面」の構築について記事を書いていこうと思っています:

本記事ではWEBサーバー環境を整える上で必要になるBrowsersyncについて説明します。


Browsersyncを利用する理由

これからはBrowsersyncを利用していきます。Browsersyncを利用する理由として次の点が挙げられます:

  1. Polymer本家のStarter KitでBrowsersyncを利用している。
  2. WEBサーバーの機能を搭載している。
  3. ファイル更新時にブラウザを自動リロードしてくれる機能がある。
  4. 追加フォルダをサーブ (供給) することができる。
  5. proxy機能を利用することでクロスドメイン対策をせずにすむ。

「2」については、WEBサーバーを搭載していることで、自身でWEBサーバーを構築しなくてもPolymerのサンプルを実行することができます。

「3」については、リソースファイル (html, css, js ファイルなど) を変更すると、変更を感知してBrowsersyncがブラウザを自動でリロードしてくれます。

「4」と「5」についてはもう少し詳しく説明します。

Note: proxyモードでBrowsersyncを起動すると、ブラウザの自動リロードが機能しなくなる場合があります。このような状況が発生した場合は「Browsersyncの自動リロード機能の不具合」を参照ください。


追加フォルダのサーブ機能

追加フォルダをサーブしたい理由ですが、CSSのベンダープレフィックスを例にして考えてみましょう。スタイルには、ブラウザ独自の拡張機能、草案段階の仕様を先行実装したものがあったりします。ブラウザベンダーはこのようなスタイルのプロパティまたは値の先頭に識別子を付けます。

このスタイルに対して:

display: flex;

ベンダープレフィックスを付加してみると、次のようになります:

display: -webkit-flex;
display: -ms-flexbox;
display: flex;

開発者が各スタイルにベンダープレフィックスが必要か否かを全て把握することはできません。このためプロジェクトをビルド (gulpやgrantで) してベンダープレフィックスを付加し、動作確認している方もいると思います。ただし毎回ビルドしてベンダープレフィックスがついたものを確認するのは大変ですし、そもそもベンダープレフィックスを意識さえしたくありません。

このような労力を省くためにBrowsersyncの「追加フォルダのサーブ」機能を利用します。下図の追加フォルダである「.tmp」はBrowsersyncによってサーブされ、.tmp配下のリソースがプロジェクトルート直下に配置されたように動作します

f:id:masterpg:20160923115306p:plain

図の説明のように、おおもとのcssやhtmlファイルの変更を感知したタイミングで、ベンダープレフィックスを付加した同名のファイルを追加フォルダに自動で生成します。こうすることでクライアントは常にベンダープレフィックスが付加されたファイルを取得するため、開発者はベンダープレフィックスを意識する必要がなくなります。

このような環境を構築する方法は「ベンダープレフィックスを意識せずに開発するための環境を整える」で説明しています。


proxy機能

上記の「追加フォルダのサーブ機能」で示した例を実現するには、本来アクセスすべきWEBサーバー以外に開発用のWEBサーバー (Browsersync) を立てる必要があります。ここで問題になるのがクロスドメインです。

開発中は、開発用のWEBサーバーから画面用リソース (.html, .css, .js など) を取得し、データ取得/更新API (顧客一覧取得、顧客データ更新 など) は本来のWEBサーバーへリクエストすることになります。このような状況では、開発用のWEBサーバーと本来のWEBサーバーのオリジンが異なってしまい、クロスドメイン対策が必要になってしまいます。

ただし、Browsersyncのproxy機能を利用すればクロスドメイン対策をせずに本来のWEBサーバーにAPIをリクエストすることができます:

f:id:masterpg:20160923115412p:plain

ここではWEBサーバーが開発用マシンと別マシンになっていますが、開発用マシンにWEBサーバーが構築されていてももちろん問題ありません。

本記事の以降では、このようなWEBサーバー構成を実現するための説明をしていきます。


Browsersyncの設定

本ページで使用するプロジェクトはgithubにアップしてあります。ここからリポジトリを自身の環境へクローンしてください。

クローンを作成したら次のフォルダへ移動します:

cd /【任意のフォルダ】/polymer-starter-kits/projects/proxy


gulpのインストール

gulpとは、CSSやJavaScriptファイルの圧縮、SassやTypeScriptのコンパイル、などを自動化するためのツールです。Polymer本家のStarter Kitでもgulpを使用していることもあり、今回のプロジェクトでも使用することにします。

gulpをインストールするにはNode.jsがインストールされている必要があるので、まずNode.jsをインストールしてください。

gulpのインストールにはグローバルとローカルとがあり、両方のインストールを行う必要があります。なぜ2つインストールしなくはならないかと疑問に思われるかもしれませんが、「そういうもの」だと思ってもらっても問題ないでしょう。「gulpのアプローチ "なぜグローバルとローカルにインストールが必要なのか"」で詳しく説明されているので知りたい方は参照ください。

まずグローバルインストールを行いましょう。コマンドを実行する場所は任意で構いません:

npm install -g gulp


npmでプロジェクトに必要なライブラリをインストール

プロジェクトルートに移動し、次のコマンドを実行してください:

npm install

このコマンドを実行することでプロジェクトルート直下の「package.json」に記述されているライブラリが自動でインストールされます。package.jsonには「gulpのインストール」で示したローカルインストールを行う設定も記述されています。


bowerでクライアントライブラリをインストール

プロジェクトルートに移動し、次のコマンドを実行してください:

bower update

このコマンドを実行することでプロジェクトルート直下の「bower.json」に記述されているクライアント側で必要なライブラリが自動でインストールされます。


WEBサーバーの構成

次はStarter KitのWEBサーバー構成とAPIリクエストの流れを示しています:

f:id:masterpg:20160923115436p:plain

WEBサーバーはeasymockを使用しています。easymockはリクエストされたAPIに対するレスポンスを簡単に返すことができる機能が備わっているので、この機能を利用し、Proxyサーバーを経由したAPIリクエストが正しく処理されるかを検証します。

ProxyサーバーにはBrowsersyncを使用し、APIリクエストの中継と、画面用リソース (.html, .css, .js など) のサーブを行います。


gulpfile.jsの内容

プロジェクトルート直下にgulpfile.jsというファイルがあり、この中に開発をする上で必要なタスク (WEBサーバーの起動、CSSやJavaScriptファイルの圧縮、などのタスク) を記述していくことになります。ではこの内容を見ていきましょう。

ファイル先頭の方では必要となるgulpプラグインを読み込んでいます:

var gulp = require('gulp');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var historyApiFallback = require('connect-history-api-fallback');
easymockについて

次はWEBサーバーであるeasymockを起動するタスクの定義です:

/**
 * easymockを起動します。
 * Browsersyncのproxy機能を検証するためのWEBサーバーです。
 */
gulp.task('serve:mock', function () {
  var options = {
    // サーバーのポート番号
    port: 5050,
    // APIリクエストに対するレスポンス用のjsonファイルを格納するフォルダ
    path: 'mock'
  };
  var MockServer = require('easymock').MockServer;
  var server = new MockServer(options);
  server.start();
});

path: 'mock'について説明します。easymockではAPIリクエストがあると、そのURLとメソッド(GET, POST など)をもとに、規則に従って対応するjsonファイルの内容をレスポンスします。path:に指定したmockフォルダにはレスポンス用のjsonファイルが格納されています。APIリクエストに対するレスポンスの例をみてみましょう。

easymockが次のようなAPIリクエスト(GET)を受信したとします:

http://localhost:5050/api/hello

easymockは、起動設定のpath: 'mock'、URLの/api/hello、メソッドが「GET」であることをもとにして、レスポンス用のjsonファイルを探します。上記のAPIリクエストに対してはmock/api/hello_get.jsonというファイルの内容をレスポンスすることになります。

Note: mockフォルダに「_get.json」というファイルを置いてあります。Browsersyncを起動するとhttp://localhost:5050/というリクエストが送られるようで、_get.jsonが存在しないと、このリクエストに対するレスポンスファイルがないという警告がでます。_get.jsonはこの警告を回避するためのファイルで、警告を気にしなければあってもなくてもよいファイルです。

Browsersyncについて

次はProxyサーバーであるBrowsersyncを起動するタスクの定義です:

/**
 * Browsersyncを起動します。
 */
gulp.task('serve', ['serve:mock'], function () {
  // Browsersyncの設定
  browserSync({
    logPrefix: 'PSK',
    notify: false,
    port: 5000,
    ui: {port: 5001},
    snippetOptions: {
      rule: {
        match: '<span id="browser-sync-binding"></span>',
        fn: function (snippet) {
          return snippet;
        }
      }
    },

    // >>> proxyサーバーの設定
    proxy: {
      // 本来アクセスすべきWEBサーバーを指定する
      target: 'localhost:5050',
      // URL指定、リロードでサイトにアクセスがあった際、
      // デフォルトファイル(index.html)を返すためのプラグインを指定
      middleware: [historyApiFallback()]
    },
    // 追加フォルダの指定
    // 左に指定したものの方が優先度が高くなる
    serveStatic: ['.tmp', '.']
    // <<<
  });

  // 変更を監視するファイルを指定
  gulp.watch(['src/**/{*.css,*.html,*.js}'], reload);
  gulp.watch(['styles/**/{*.css,*.html}'], reload);
});

gulp.task('serve', ['serve:mock'], ...にある['serve:mock']ですが、serveタスク (Browsersyncの起動) が行われる前に、serve:mockタスク (easymockの起動) が行われることを意味します。


port: 5000は自身が作成するサイトのポート番号です (ここではStarter Kitのサイト) 。ui: {port: 5001}はBrowsersyncの管理コンソールのポート番号です。


snippetOptionsはBrowsersyncが必要なコードを埋め込む挿入ポイントを指定しています。Starter Kitフォルダの直下にあるindex.htmlに<span id="browser-sync-binding"></span>という記述があり、ここが挿入ポイントになります。


proxy:の中のtarget: 'localhost:5050'は本来アクセスすべきWEBサーバーを指定します。ここでは検証用にeasymockのサーバーを指定していますが、実際の開発ではこの部分を書き換えてください。


proxy:の中のmiddleware: [historyApiFallback()] (connect-history-api-fallback) はURL指定やリロード時のデフォルトファイルを決定してくれるプラグインです。今までのWEBアプリケーションではURLごとにページが生成されるため、そのURLを直接指定すれば目的のページが表示されました。ただしSPA (シングルページアプリケーション) では「index.html」が全ての入り口になり、ここを経由して目的のページへ遷移することになります。このため次のようなURLが要求された場合、みなindex.htmlがレスポンスされること期待します:

  • http://localhost:5000/home
  • http://localhost:5000/users
  • http://localhost:5000/users/0001?first=Bob&last=Smith

connect-history-api-fallbackプラグインは、このようなURLに対するデフォルトファイルのレスポンスを判断し、決定してくれます。


serveStatic: ['.tmp', '.']は、proxy指定された場合のみ有効なオプションで、追加でサーブするフォルダを指定します。左側で指定したフォルダの方が優先度が高くなるので、この指定ではカレントディレクトリ.より、.tmpフォルダに格納されるリソースが優先してレスポンスされます。ここではStarter Kit直下の.tmpフォルダを指定してますが、Starter Kit直下にかぎらず任意の場所のフォルダをサーブすることが可能です。本記事のStarter Kitではまだ.tmpフォルダは使用されませんが、今後使用することになります。

APIリクエストのコーディング

APIリクエストしている場所をみてみましょう。次はAPIリクエストをしているコーディングの抜粋です:

【src/elements/app-view.html】

<!-- APIリクエストを行う送信ボタン -->
<paper-icon-button icon="send" on-tap="_sendButtonOnTap"></paper-icon-button>

...

<!-- APIリクエストを行うAjaxエレメント -->
<iron-ajax
  id="helloAjax"
  url="/api/hello"
  method="GET"
  handle-as="json"
  on-response="_helloAjaxOnResponse"></iron-ajax>

【src/elements/app-view.js】

// 送信ボタンがタップされた際のハンドラ
_sendButtonOnTap: function (e) {
  // hello APIをリクエスト
  this.$.helloAjax.generateRequest();
},

// hello APIのレスポンスハンドラ
_helloAjaxOnResponse: function (e) {
  // hello APIのレスポンスをメッセージ表示
  var responseData = this.$.helloAjax.lastResponse;
  alert(responseData['message']);
}


Browsersyncの起動

Browsersyncを起動し、Starter Kitのサイトを表示してみましょう。

Start Kitフォルダに移動し次のコマンドを実行します:

gulp serve

コマンドを実行するとコンソールに次のような出力がされます:

f:id:masterpg:20160923115544p:plain

Starter Kitのサイトにアクセスします:

  • http://localhost:5000
  • http://192.168.1.21:5000 (自身のマシンのアドレス)

Starter Kitのサイトが表示されたら、右上の送信アイコンをクリックします。キャプチャのようなメッセージボックスが表示されたら、proxy経由でAPIリクエストが成功したことを意味します。

f:id:masterpg:20160923115622p:plain


Browsersyncの管理コンソールは次のURLで表示できます。どのような機能があるか確認してみると良いでしょう:

  • http://localhost:5001
  • http://192.168.1.21:5001 (自身のマシンのアドレス)


Browsersyncの自動リロード機能の不具合

Browsersyncにはファイル更新時にブラウザを自動リロードしてくれる機能がありますが、proxyモードでBrowsersyncを起動するとほとんどの場合機能しなくなります。調べてみた結果、snippetOptionsで指定したBrowsersyncのコードの挿入ポイントに、コードが挿入されなくなるのが原因のようです。この不具合に対応する方法を紹介しますが、Browsersyncのバージョンが上がったりすると機能しなくなるかもしれませんので、あらかじめご了承ください。

まずStarter Kit直下の「gulpfile.js」で、コメントの指示に従って修を正行います。これでproxyモードではなく、一般モードでBrowsersyncが起動するようになります:

【gulpfile.js】

// ↓ このセクションをコメント化
// >>> proxyサーバーの設定
//proxy: {
//  // 本来アクセスすべきWEBサーバーを指定する
//  target: 'localhost:5050',
//  // URL指定、リロードでサイトにアクセスがあった際、
//  // デフォルトファイル(index.html)を返すためのプラグインを指定
//  middleware: [historyApiFallback()]
//},
//// 追加フォルダの指定
//// 左に指定したものの方が優先度が高くなる
//serveStatic: ['.tmp', '.']
// <<<

// ↓ このセクションのコメントを外し有効にする
// >>> 一般的なWEBサーバーの設定
server: {
  baseDir: ['.tmp', '.'],
  middleware: [historyApiFallback()]
}
// <<<

Start Kitフォルダに移動し次のコマンドを実行してStarter Kitを起動します:

gulp serve

次のURLでStarter Kitを表示します:

  • http://localhost:5000
  • http://192.168.1.21:5000 (自身のマシンのアドレス)

Starter Kitが表示されたら、ブラウザの開発ツールでBrowsersyncによって挿入されたコードをコピーします (下図の赤枠の部分) :

f:id:masterpg:20160923115714p:plain

Note: 一般モードでBrowsersyncを起動しても、コードが挿入されないことが多々あります。このような場合は、リロード、キャッシュクリア、ブラウザ再起動などを試してみてください。

「index.html」で、既存のBrowsersyncの挿入ポイントをコメント化し、その下に上記でコピーしたコードを追加します:

【index.html】

...
<body unresolved class="fullbleed layout vertical">
  <!-- 以下はBrowsersyncに必要なコードを埋め込む挿入ポイントを示している -->
  <!-- この部分をコメント化
  <span id="browser-sync-binding"></span>
  -->
  <!-- コピーしたコードをここに追加 -->
  <script async="" src="/browser-sync/browser-sync-client.2.9.11.js"></script>
  ...
</body>

最後に「gulpfile.js」で、proxyモードでBrowsersyncが起動するように戻します:

【gulpfile.js】

// ↓ このセクションのコメントを外し有効にする
// >>> proxyサーバーの設定
proxy: {
  // 本来アクセスすべきWEBサーバーを指定する
  target: 'localhost:5050',
  // URL指定、リロードでサイトにアクセスがあった際、
  // デフォルトファイル(index.html)を返すためのプラグインを指定
  middleware: [historyApiFallback()]
},
// 追加フォルダの指定
// 左に指定したものの方が優先度が高くなる
serveStatic: ['.tmp', '.']
// <<<

// ↓ このセクションのコメント化
// >>> 一般的なWEBサーバーの設定
//server: {
//  baseDir: ['.tmp', '.'],
//  middleware: [historyApiFallback()]
//}
// <<<


以上で、proxyモードでBrowsersyncを起動した場合でも、ブラウザの自動リロードが機能するようになります。