どうも、最近スマホゲー界隈からコンバートしてきた新米フロントエンド戦士です。
数年前はブラウザゲー界隈にいたんですが…完全に浦島太郎状態です。
ここ数ヶ月で色々学ばせてもらいつつ、自分なりのNode開発環境が整ったので公開しちゃいます。
完成した環境はこちらのv1.0(masterの最新はこちらでクロスプラットフォーム対応を行ったものになります)
2018/8/28 追記
本日 Babel7がリリースされました!
それに伴いパッケージ名が変更されていました。
babel-core
-> @babel/core
babel-preset-env
-> @babel/preset-env
また
babel-preset-env2.0でbabel-polyfillは不要になるらしい
の件が実験的機能として導入され、以下のようにuseBuiltIns:usage
と設定すると必要に応じて自動でpolyfillしてくれるようになりました。
presets: [
[
'@babel/preset-env', { 'useBuiltIns': 'usage', ... }
]
]
目指す開発環境
以下のような環境を目指しました。
- 分かりやすくシンプルに
- 最新のECMAScriptを使える
- ローカルサーバーをサクッと立てる
- プログラムも静的ファイルも変更監視で自動リロード
各項目について説明しいきます。
分かりやすくシンプルに
いろんなNodeモジュールが公開されていて色々試したくなりますが、目指すところは極力シンプル!
使うモジュールはこんな感じです。
"dependencies": {
"babel-polyfill": "^6.26.0"
},
"devDependencies": {
"cpx": "^1.5.0",
"mkdirp": "^0.5.1",
"rimraf": "^2.6.2",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"webpack": "^3.5.6",
"webpack-dev-server": "^2.7.1"
}
ザックリ分類すると
- ファイル/ディレクトリ操作系:
cpx
mkdirp
rimraf
- ビルドツールのwebpack系:
webpack
webpack-dev-server
- トランスパイラのbabel系:
babel-core
babel-loader
babel-preset-env
babel-polyfill
タスクランナー系は使わず、npm-scriptでなんとかする方針でいきます。
そしてディレクトリ構造はこんな感じにしました。
root/
├─ src/ ビルド前のindex.htmlやプログラム類を置く
├─ assets/ ビルド前の画像などのリソース類を置く
└─ dist/ ビルド後のindex.htmlやプログラム類とassets配下のコピーを置く
ビルド前のプログラムやリソースと、ビルド後の生成物をきっちり分けられるようにしました。
distディレクトリはビルド毎にまるっと削除して作り直します。
モジュールの説明
mkdirpとrimraf
バンドル後のコードや画像など、最終的な精製物を配置するdistディレクトリの初期化に使います。
クロスプラットフォームなディレクトリ作成/削除モジュールです。
下記コマンドでdistディレクトリの削除と作り直しができます。
rimraf dist/ && mkdirp dist/
cpx
画像など素材をdistディレクトリにコピーするために使います。
さらに、コピーした元ファイルの変更監視もできます。
~~こちらもクロスプラットフォームだと見た記憶がありますが、未確認です。(知ってる方いたら教えてください)~~クロスプラットフォームで使えます。(こちらで確認しました)
下記コマンドでsrc配下のindex.htmlとassets配下のリソース類をdist配下に配置できます。
cpx src/index.html dist/
cpx 'assets/**/*' dist/
webpack + babel-core + babel-loader
ES2015構文のimport/exportを使ってプログラムをモジュール化するために使います。
webpackでビルドするとモジュールが結合されて1つのJavaScriptファイルになります。
babel-polyfill
babel-loaderの構文変換だけでは補えない**新機能(Promiseとか)**を補ってくれます。
babel-preset-env
非推奨のbabel-preset-es2015やbabel-preset-es2017に代わるpresetで、最新ECMAScript構文が使えるようになります。
また、ビルドターゲットがサポートしていない構文のみ無駄なく変換してくれます。
最新のECMAScriptを使える
import/exportだけでなく最新のECMAScript構文を使うために、babel-polyfill
とbabel-preset-env
を設定します。
babel-polyfillの設定
entry: [
'babel-polyfill',
path.resolve('src', 'index.js')
],
entryにindex.jsと共に書いておけば、一緒にバンドルしてくれます。
ただし、これだとbabel-polyfillで用意している全機能がバンドルされ、ビルド後のファイルが少し大きくなってしまいます。
index.jsファイル内の先頭行でimportしてbabel-loaderのuseBuiltInsオプションを付けることで使用する機能のみバンドルすることも出来ますが、babel-preset-env2.0でbabel-polyfillは不要になるらしいので、それまではシンプルさ重視にしました。
babel-preset-envの設定
module: {
rules: [
{
test: /\.js$/,
exclude: path.resolve('node_modules'),
loader: 'babel-loader',
options: {
presets: [
['env', {
'targets': {
'node': 'current', // 動かしてるPCのNodeバージョン
'browsers': 'last 2 versions' // 各種ブラウザの直近2バージョン
}
}]
]
}
}
]
},
babel-loaderのpresetとしてenv
を設定します。
targetsとして開発端末のNodeバージョンと、各種ブラウザの直近2バージョンで動くコードにトランスパイルするよう設定しています。
ローカルサーバーをサクッと立てる
ローカルサーバーはwebpack-dev-server
でサクッと立てます。
webpack-dev-serverの設定
devServer: {
port: 8000, // http://localhost:8000として立てる
open: true // ビルド後にブラウザでhttp://localhost:8000を開く
}
上記設定を追加し、webpack
の代わりにwebpack-dev-server
をコマンド実行すればビルド後のアプリケーションがhttp://localhost:8000で立ち上がります。
プログラムも静的ファイルも変更監視で自動リロード
ビルド対象のindex.jsが依存関係を持つプログラムに関しては、webpack-dev-server
を使うだけで変更があったら自動的にビルド & ブラウザリロードしてくれます。
しかし、任意のタイミングで読み込みたい画像などの静的ファイルは、別途工夫が必要です。
webpack-dev-serverの設定追加
devServer: {
port: 8000,
open: true,
contentBase: path.resolve('dist'), //追加
watchContentBase: true //追加
}
distディレクトリをwatchする追加を設定しました。これでdist配下のファイルはプログラム以外も変更監視の対象になります。
しかし、静的ファイルはassetsディレクトリで管理してビルド時にdist配下にコピーする方針なので、dist配下で直接変更は行われません。
そこでdist配下へのコピー時にcpxの変更監視機能を使用します。
cpxの変更監視設定
cpx src/index.html dist/ -w
cpx 'assets/**/*' dist/ -w
-w
オプションを付けることで、コピー元に変更があったらdist配下に自動的にコピーされるようになります。
dist配下にコピーされると、webpack-dev-server
が変更検知してブラウザリロードを行ってくれるという寸法です。
npm-scriptについて
最後に、npm-scriptの説明も軽くしておきます。
"scripts": {
"start": "npm run mkdist && npm run cpassets -- -w & npm run cphtml -- -w & webpack-dev-server",
"build": "npm run mkdist && npm run cpassets && npm run cphtml && webpack --optimize-minimize",
"mkdist": "rimraf dist/ && mkdirp dist/", // startとbuildから呼ばれる
"cpassets": "cpx 'assets/**/*' dist/", // startとbuildから呼ばれる
"cphtml": "cpx src/index.html dist/" // startとbuildから呼ばれる
}
npm run start(npm startでもOK)
dist掃除〜webpack-dev-server起動を行います。
mkdistでdistを掃除完了後、cpassetsとcpassetsに-w
オプションを付けつつ、webpack-dev-serverと&
で繋ぎ並列実行させています。
npm run build
dist掃除〜ビルド&圧縮してdistに配置を行います。
監視が必要ないのでstartとは異なり、mkdist cpassets cphtmlを直列で行った後、ファイル圧縮の--optimize-minimize
オプション付きでwebpackを実行します。