- プロジェクトのルートに
dist
フォルダを作り、以下のindex.html
ファイルを追加します:
<!doctype html>
<html>
<head>
</head>
<body>
<div class="app"></div>
<script src="client-bundle.js"></script>
</body>
</html>
src
フォルダには、次のサブフォルダを作ります: server
、shared
、client
。そしてカレントにあるindex.js
をserver
フォルダに、dog.js
をshared
フォルダに移動します。client
フォルダにはapp.js
ファイルを作ります。
Nodeバックエンドは使わないのですが、この分割はそれぞれの所属をはっきりさせるのに役立ちます。server/index.js
内のimport Dog from './dog';
をimport Dog from '../shared/dog';
に変更する必要があり、そうしなければESLintは解決できないモジュールのエラーを検出してくれます。
client/app.js
ファイルに以下を書きます:
import Dog from '../shared/dog';
const browserToby = new Dog('Browser Toby');
document.querySelector('.app').innerText = browserToby.bark();
package.json
のeslintConfig
に以下を追加します:
"env": {
"browser": true
}
こうすると、window
やdocument
といったブラウザでは必ずアクセスできる変数を使っていても、ESLintは未定義変数の警告を出さないようになります。
さらに、Promise
など、最新のESの仕様を使いたい場合には、Babel Polyfillをクライアントコードに含める必要があります。
yarn add babel-polyfill
を実行します
そしてapp.js
の先頭に、次のようなimportを追加します:
import 'babel-polyfill';
このpolyfillを使うと、バンドルのサイズが増えるため、これらの機能を使いたい場合のみ追加するようにします。このチュートリアルでは無駄のないボイラープレートのコードを提供するために、このpolyfillを導入し、次章以降のコードサンプルで使っていきます。
Node環境では、いろんなファイルを自由にimport
しても、Nodeはファイルシステムを使って適切に解決してくれました。一方ブラウザでは、ファイルシステムがないため、import
もファイルを参照することができません。エントリポイントのファイルであるapp.js
が必要なインポートのツリー構造を探してこれるよう、依存関係のツリー全体を1つのファイルに「バンドル」しなければなりません。このためのツールがWebpackです。
WebpackはGulp同様、webpack.config.js
という設定ファイルを使用します。GulpはBabelを利用してES6のimportとexportを使えるようにしていましたが、Webpackも同様のことができます。そのためには、webpack.config.babel.js
という名前のファイルを使います。
-
空のファイル
webpack.config.babel.js
を作ります -
Gulpの
link
タスクにwebpack.config.babel.js
を、paths
定数にいくつかパスを追加します。
const paths = {
allSrcJs: 'src/**/*.js',
gulpFile: 'gulpfile.babel.js',
webpackFile: 'webpack.config.babel.js',
libDir: 'lib',
distDir: 'dist',
};
// [...]
gulp.task('lint', () =>
gulp.src([
paths.allSrcJs,
paths.gulpFile,
paths.webpackFile,
])
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError())
);
WebpackにBabelを使ってES6ファイルを扱う方法を教える必要があります(GulpにES6ファイルの扱いを教えるためにgulp-babel
を使ったのと同様です)。Webpackでは、古い素のJavaScriptではないものを扱う場合、loadersを使います。Webpack用のBabel loaderをインストールしてみましょう。
-
yarn add --dev babel-loader
を実行します -
webpack.config.babel.js
ファイルに以下を書きます:
export default {
output: {
filename: 'client-bundle.js',
},
devtool: 'source-map',
module: {
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
exclude: [/node_modules/],
},
],
},
resolve: {
extensions: ['', '.js', '.jsx'],
},
};
これを解読してみましょう:
このファイルはWebpackが読めるようにexport
する必要があります。output.filename
が生成したいバンドルのファイル名です。devtool: 'source-map'
はブラウザでのデバッグが捗るようにするためのソースマップを有効にします。module.loaders
にはtest
があります。これはbabel-loader
がどんなファイルを扱うかテストするかを正規表現で指定しています。次章では.js
ファイルと(React用の).jsx
ファイルの両方を扱うため、/\.jsx?$/
という正規表現を使っています。node_modules
フォルダはトランスパイルを行わないため、excludeで排除しています。resolve
の部分では、拡張子なしでimport
した場合、どのような種類のファイルをimport
するべきかをWebpackに指定しています。これは例えばimport Foo from './foo'
と書くと、foo
のところはfoo.js
やfoo.jsx
と解釈されるようにするためのものです。
さて、Webpackの設定は終わりましたが、実行するにはもうちょっとかかります。
Webpackはさまざまなことを行います。プロジェクトがクライアントサイドのものであれば、Gulpをすっかり置き換えてしまうこともできます。一方で、Gulpはもっと汎用的なツールで、linting、テスト、バックエンドタスクなどに向いています。初学者の人にとっては、Webpackの複雑な設定よりもシンプルで理解しやすいでしょう。ここではすでにGulpのセットアップとワークフローを構築済みなので、WebpackをGulpのビルドに統合させるのが理想的でしょう。
それではWebpackを実行するGulpタスクを作りましょう。gulpfile.babel.js
を開きます。
node lib/
を実行するmain
タスクはもう必要ありません。index.html
を開けばアプリが実行されるためです。
import { exec } from 'child_process'
を削除します
Gulpプラグイン同様、webpack-stream
パッケージを使えば、GulpにWebpackに統合するのも簡単です。
-
yarn add --dev webpack-stream
を実行し、パッケージをインストールします -
以下の
import
を追加します:
import webpack from 'webpack-stream';
import webpackConfig from './webpack.config.babel';
2行目で設定ファイルを取り込んでいます。
先ほど説明したとおり、次章では.jsx
ファイルを使います(まず使うのはクライアント側でですが、サーバ側でも後ほど使用します)。ちょっと早いですがさっそく設定しておきましょう。
- constの定数宣言を次のようにします:
const paths = {
allSrcJs: 'src/**/*.js?(x)',
serverSrcJs: 'src/server/**/*.js?(x)',
sharedSrcJs: 'src/shared/**/*.js?(x)',
clientEntryPoint: 'src/client/app.js',
gulpFile: 'gulpfile.babel.js',
webpackFile: 'webpack.config.babel.js',
libDir: 'lib',
distDir: 'dist',
};
.js?(x)
は.js
ファイルと.jsx
ファイルにマッチするパターンです。
アプリケーションの別々に分けられた部分についてと、エントリポイントのファイルが定数に追加されました。
main
を次のように変更します:
gulp.task('main', ['lint', 'clean'], () =>
gulp.src(paths.clientEntryPoint)
.pipe(webpack(webpackConfig))
.pipe(gulp.dest(paths.distDir))
);
注意: build
タスクは現状、src
以下にある全ての.js
ファイルをES6からES5にトランスパイルします。ここではコードをserver
とshared
、client
に分割しています。これはserver
とshared
のみこのタスクでコンパイルするためのものです(client
はWebpackが面倒を見るためです)。しかしながら、テスティングの章では、Webpackの外でテストするためclient
のコードもGulpでコンパイルする必要があります。そのため、その章にたどり着くまで、不要な重複ビルドが行われることになりますが、今のところはこれでも良いと同意してもらえると思います。実際には、今ここで扱うのはクライアントバンドルのみなので、build
タスクもlib
フォルダもその章までは特に使う必要がないものです。
yarn start
を実行すると、Webpackがclient-bundle.js
ファイルを作ります。index.html
をブラウザで開くと"Wah wah, I am Browser Toby"と表示されるはずです。
最後に: lib
フォルダとは異なり、dist/client-bundle.js
ファイルとdist/client-bundle.js.map
ファイルはビルドの度にclean
タスクで消されません。
clientBundle: 'dist/client-bundle.js?(.map)'
をpaths
の設定に追加し、clean
タスクを以下のように変更します:
gulp.task('clean', () => del([
paths.libDir,
paths.clientBundle,
]));
/dist/client-bundle.js*
を.gitignore
ファイルに追加します。
(原文: 7 - Client App with Webpack)
次章: 8 - React