- プロジェクトのルートに
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