APOC

Angular CLIでサブディレクトリにビルドする方法

二重の意味でAngularにハマってきた今日このごろですが、ようやく年末から年度末にかけての地獄のような日々から開放されました。ずっと頭の中でマクロスFのライオンをリピート再生して「い”ぎたいっ」て思ってました。

そんなこんなで余裕がでて来たものの、「休日の休み方忘れたな〜何しようかな〜」と思っていたらブログの存在を思い出した次第です。

Angular CLIの概要

Angular CLIはコマンドラインからAngularのコンポーネントを作成したりビルドしたりするツールです。いまのところNode.js v6.9.0以上で動きます。
AngularやReactなど最新のJavaScriptのフレームワークやライブラリはビルド環境を構築しないと使えないのですが、この環境構築もNode.jsを使用したGulpやWebpackなどを使用することが多く初めての人はこの段階でヒィヒィ言わされることになります。なりました。

Angular CLIはそういった環境構築の部分をコマンド一発で済ませたり、Angularで使用するコンポーネントやサービスなどを作ることができます。

ローカルでのプレビューやプロダクト環境用のビルドなども出来るのでかなり便利です。

コマンドラインでの操作に慣れていた方がいいですが、慣れていなくてもGithubのREADME.mdの通り使えばサクッとAngularが動いてくれます。

サブディレクトリにビルドしたい

そんなAngular CLIですが、ビルドするとフォルダごと再生成されるためビルド先にAngularとは関係のないファイルあると削除されてしまいます。

想定としてはLaravelなどバックエンドのフレームワークを使用する場合に、Angularはpublicフォルダ(Document Root)に置く必要がありますが、publicフォルダにはフレームワーク用のindex.phpや.htaccessが置いてあったりしますので、これらのファイルがビルドするときに消えることになります。

こういった場合にはビルド先をpublicフォルダのサブディレクトリにしてフォルダを分けて管理した方がいいでしょう。

なお、URLにサブディレクトリを含める場合と含めない場合とで少し方法が変わるようなので、その2パターンを紹介します。

サブディレクトリにビルドしてURLにサブディレクトリ名を含める場合

この場合はng buildのオプションとして--output-path--base-hrefを指定します。

これだけでOKですが、オプションと注意点の説明ちょこっとしておきます。

–output-pathオプションについて

ビルド先のディレクトリを指定します。
ビルド先はデフォルトだと.angular-cli.jsonに記載されている"outDir": "dist/"となりますが、このオプションを付けることで変更できます。

ちなみに.angular-cli.jsonの方を書き換えてもいいのですが、今回はオプションの方を採用します。

ビルド先のパスはng buildを実行したフォルダがカレントとなる(プロジェクトフォルダになるはず)ため、プロジェクトフォルダ/dist/subを指定したい場合は./からパスを開始して--output-path=./dist/subとします。

–base-hrefオプションについて

これはHTMLタグの<base href="{path}">のpathを指定します。
Angularの動作にはbaseタグの設定が必須で、Angular CLIを使用してプロジェクトを作成すると<base href="/">が初期設定としてプロジェクトフォルダのindex.htmlに記述されています。

baseタグのhrefが/のままだと、リソースなどをドキュメントルートからのパスで読み込もうとするため、ファイルが見つからずエラーとなります。

このオプションはドキュメントルートからのサブディレクトリのパスを指定するため--base-href=/sub/とします。

注意点について

通常、base hrefをサブディレクトリに指定してもCSSのbackground-imageのパスには適用されません。そのため、Angular CLIでは--base-hrefの値に合わせてbackground-imageのパスを直接書き換えているようです。

しかし、HTMLのimgタグのsrc属性は書き換わらないため、パスの記述をカレントディレクトリからの開始を示す./から始める必要があります。

続いてもう1パターンの紹介です。

サブディレクトリにビルドしてURLはドキュメントルートにする場合

この場合は少しややこしいことになりますが、まずはとにかく動かしてみましょう。

Angular CLIではビルドすると依存関係のjsファイルを読み込むscriptタグを自動的にHTMLへ挿入します。
サブディレクトリにビルドしてURLはドキュメントルートという場合、index.htmlはドキュメントルートへ配置することになりますが、scriptタグのsrcはカレントディレクトリから開始となるため、パスが繋がりません。そこで--deploy-urlを使用してパスを変更します。

以上のことを踏まえて、次のコマンドでパスの変更を行ったファイルをサブディレクトリへビルドします。

このままではindex.htmlが/sub/index.htmlに存在するため、/index.htmlに移動します。
これでドキュメントルートのURLにアクセスするとAngularが実行されます。

が、ng buildではなくng build --prodでプロダクト環境用のビルドを行うと依存関係ファイルの名前がハッシュ化されるため、毎回index.htmlを移動することになります。

この問題はPHPのget_file_contentsやincludeを使用して解決したり、Gulpを使ってファイル監視して移動させるなどいくつかアプローチがありますので自分の環境に合わせて決めるといいでしょう。

開発サーバと実サーバが両立できない問題

Angular CLIではng serveというコマンドで開発サーバを立ち上げてそこでプレビューすることが基本的な使い方となっていますが、この場合はビルドされたファイルはディスクではなくメモリー上に存在する状態になります。

このコマンドでは--output-pathオプションを使うことができない(Wikiには載ってますが・・・)ようで、ビルドしたときとディレクトリが変わってしまい、開発サーバと実サーバとで表示が両立できませんでした。

解決策として.angular-cli.jsonで画像などのアセットを開発サーバ用と実サーバ用と2重に書き出す方法もありますが、同じファイルが存在してしまうため格好悪く、あまりよろしくありません。

そこでng serveでの開発サーバは使用せずbuildコマンドの--watchオプションでファイル変更を監視して自動ビルドを行い、サーバの用意やブラウザの自動更新はNode.jsのBrowserSyncで代用します。

–watchオプションの使い方

使い方は簡単でng buildの後ろにくっつけます

これでファイルの更新があると自動的にビルドされるようになります。差分ビルドなので初回より短時間で済みます。

Browser Syncの用意

1.インストール

npmやyarnをコマンドでインストールします。

2.設定ファイル(bs-config.js)の作成

以下のコマンドで設定ファイル(bs-config.js)を作成します。

3.ドキュメントルートと監視ファイルの設定

bs-config.jsを開くといろいろと設定が書いてありますが、filesserverのところを変更します。
filesが監視ファイルの設定で、serverがドキュメントルートなどの設定になります。

4.package.jsonにコマンドを追加する

npm runで実行できるようにpackage.jsonのscriptsにコマンドを追加します。

build-watchbrowser-syncが追加したコマンドです。

5.実行する

これで準備ができたので、さきほど追加したコマンドを実行します。

2つ実行するのが面倒な場合はnpmパッケージのconcurrentlyを使ってもいいと思います。

6.実体ファイルがない場合のフォールバックを設定する

5番まででとりあえず動作はしますが、AngularなどのSPAでは下層ページのURLには実体ファイルが存在しないため、ブラウザを更新するとファイルが見つからずnot foundになります。

そのため、実体ファイルがない場合には/index.htmlにフォールバックするようにします。

フォールバックにはconnect-history-api-fallbackを使用します。これもnpmで配布されているため、Browser Syncと同じようにnpmかyarnででインストールします。

インストール終わったらbs-config.jsでrequireしてserverのmiddlewareとして加えます。

これで実体ファイルがないURLにアクセスした場合に/index.htmlへフォールバックするようになります。

おわりに

最後の方で紹介した–watchとBrowser Syncを使った方法を使うとExternal URLが発行されるため、他のパソコンや仮想環境のWindowsでクロスブラウザチェックしやすくなるのでおすすめです。

サブディレクトリにビルドしてURLはドキュメントルートにする場合の方法がややこしいので、本当はもっと簡単な方法があるんじゃないか。と疑ってますが、GithubのWikiを眺めてる感じでは見当たらず、紹介した方法で解決しました。

もっと簡単な方法があれば誰か教えてください。
ではでは ( ͡° ͜ʖ ͡° )ノシ