React+Reduxにも触れてみる 2. 一から始める環境構築
前提
これの続き。
しかしやってみたけどこれ、本当に開発環境構築というか。webpackがgcc相当でjsx(es6)のcodeからjsというbinaryを生成している気分だもう。package.jsonのdevDependenciesにbuildに必要なものを定義していって、webpackのconfigにlinker的な設定を詰め込んでいくという。
react-redux環境を作るにはstarter kit的なものもいろいろあるけど、正直あわないので一から作る(端折った)。詳しくは前回参照。
最終的な成果物
いろいろ端折って結果だけ見たい場合はこちら。
これをgit cloneしてnpm install
してmake run
してhttp://localhost:3000
を見ればもう動くものができあがっている。
ここから先はいつも通り混乱の記録である。あ、でも今回は完成した後に記事書いているから割とまとまってしまったかも。
で、どのような環境を組むか
まあそんなこと言っても何が必要なのかわかっていたら苦労はないという。とりあえず何をするにしてもnode環境を整えないとどうしようもないので、initから。
その前にnodebrewで node v6.10.1をいれる。nodebrewはplenvとかpyenvとかと同じようなもの。入れ方はどっかに書いた気がする。……気のせいだった。まあこの際なので書くか。
NODE環境の準備(MAC版)
このあたりに書いてあることをやるだけ。
brewで入れている場合は紛らわしいので削除
$ brew uninstall nodebrew $ brew uninstall node
Install
$ curl -L git.io/nodebrew | perl - setup
consoleが起動するときのscript(.bashrcなり.zshenvなり)に以下を追記。
export PATH=$HOME/.nodebrew/current/bin:$PATH
次に公式推奨版のnodeをいれる。
https://nodejs.org/ja/ ここを見ると執筆時点での推奨版はv6.10.1らしいのでそれを使うことにする。
$ nodebrew install-binary v6.10.1 $ nodebrew use v6.10.1
これでおわり。installされたかはnode -v
で確認できる。
Module Install時にerrorが出た場合
たまに以下のような文句を言われることがある。proxy使っているとかsquid的なものがいるとよくある。
npm ERR! code SELF_SIGNED_CERT_IN_CHAIN npm ERR! self signed certificate in certificate chain npm ERR! npm ERR! If you need help, you may report this error at: npm ERR! <https://github.com/npm/npm/issues> npm ERR! Please include the following file with any support request: npm ERR! /Users/kuboki/Public/working/switch/dashboard/node/npm-debug.log
この場合npm set strict-ssl false
を実行すると通るようになる。
Projectの作成
適当なdirectoryでnpm init
と入力し、質問に答えていくとそこにprojectが作られる。
関連moduleのinstall
さていよいよreactに関係しそうなものを入れていく。
compileで使うsourceに関連したlibraryはnpm install --save ****
でinstallし、開発時に使うwebpackとかのbuildに関係するtool類はnpm install --save-dev ***
でいれる。前者はpackage.jsonの中でdependencies
の中にhashで定義され、後者はdevDependencies
の中に定義される。こうしとくとまあ、このprojectでのbuildに必要な者とsourceに関わっているlibraryとがあとで判別しやすい。
さてじゃあいれていこう。react関係は、と。
$ npm install --save react react-dom react-redux
で、build環境関係は、と。
$ npm install --save-dev webpack path $ npm install --save-dev babel-loader babel-core babel-preset-react babel-preset-es2015
このあたりをいれてみる。babelというのはes6のこと。es6でsourceを書きたい場合はこれが必要で、compileするときにこれらが関わってくる。で、webpackはsourceをcompileしたあとで最終的な成果物としてのjsを生成してくれる。
ということで次はこのwebpackとかを使えるようにしていこう。sourceを書くまでの道のりが遠い。
設定fileの作成
全体像
なんだかこのあたりの説明しようとしたらdirectory structure書かないとわかりづらい気がしたので一度提示してみる。ちなみにこれは完成したものでこの時点では/config
とか/src
とかないものがある。
% tree -a -I "node_modules|tmp" . ├── .babelrc ├── .gitignore ├── Makefile ├── config │ ├── entry.js │ ├── webpack-dev.config.js │ ├── webpack-production.config.js │ └── webpack.config.js ├── package.json ├── pages │ └── index.html └── src ├── components │ ├── HelloWorld │ │ └── index.jsx │ └── ReflectForm │ ├── FormApp.jsx │ ├── FormDisplay.jsx │ ├── FormInput.jsx │ └── index.js └── route └── test └── index.jsx
で、webpackを使えるようにするには.babelrc
でbabelの設定をして、config/webpack*.config.js
を作ってwebpackの設定を作る必要がある。
.babelrc
これはまあまだよく意味がわからないけどこう書けばいいらしいので一度そのまま書く。
{ "presets": ["react", "es2015"] }
ただしこれだけだとあとでimportが相対pathでしか書なくてひどいことになった。ということで探してみた。
babel-root-importをいれればいいらしい。
嘘だった。今ではbabel-root-slash-import
になっている。
$ npm install --save-dev babel-root-slash-import
これでよし。それで設定を追加して。
{ "presets": ["react", "es2015"] "plugins": [ ["babel-root-slash-import", { "rootPathSuffix": "src/" }] ] }
これでよし。
/src/webpack.config.js
web上によくあるのはwebpack1の設定方法。webpack2だとやや定義が違ったので修正していく。するとmodule.lodersをmodule.useに置き換えたりする必要が出てくる。
あとoutput.pathが絶対pathの必要があったのでそちらも修正。
const path = require('path'); module.exports = { entry: { test: path.resolve(__dirname, '../src/route', 'test', "index.jsx"), }, output: { path: path.resolve(__dirname, '../dist'), filename: '[name].js' }, devtool: 'inline-source-map', resolve: { extensions: ['.js', '.jsx'] }, module: { rules: [ { test: /\.jsx*$/, exclude: /node_modules/, loader: 'babel-loader' } ] } };
基本的にいじるのはentryの中身だけ。outputで定義したところにfileが生成される。このとき[name].js
となっているけどこの[name]
がentry以下で定義されているhash keyになる。だから/src/route/test/index.jsx
を起点にcompileされたjavascriptは/dist/test.js
という名前で生成される。
僕はtest.htmlはtest.jsを呼び、xxx.htmlはxxx.jsを呼ぶ、みたいにpage単位でloadされるjsを作っていくつもりなので、こういう定義になる。
他のwebpack.config.js
で、webpackがどの設定をするかはcommand optionで定義できる。そのとき開発用に作ったwebpack-dev.config.js
を呼び出してもいいし、余計なものをそぎ落としたwebpack-production.config.js
を使ってもいい。
でもこれらのfileでいちいち同じようにentryを定義していくのは面倒。
なので分割する。同じdirにentry.js
というのを作って、それを呼び出す形に変える。
以下のpageを参考にrequreで読んでみた。
entry.js
const path = require('path'); const entry = { test: path.resolve(__dirname, '../src/route', 'test', "index.jsx"), }; module.exports = entry;
webpack.config.js
const path = require('path'); const entry = require('./entry'); module.exports = { entry: entry, output: { path: path.resolve(__dirname, '../dist'), filename: '[name].js' }, devtool: 'inline-source-map', resolve: { extensions: ['.js', '.jsx'] }, module: { rules: [ { test: /\.jsx*$/, exclude: /node_modules/, loader: 'babel-loader' } ] } };
これでよし。その他のfileはあとで紹介するgit repositoryを見て欲しい。具体的にはここ。
ついでにlocal serverもいれておく
だってそうすると外のserverにいちいちuploadしなくてもtestできるので。
webpack-dev-server は上記に加えて
ローカルサーバーも起動してくれる(中身は Node.js の Express サーバーらしい) ファイルの変更を検知して自動ビルドした後、ブラウザも自動的にリロードしてくれる(Automatic Refresh) ブラウザ全体のリロードではなく、編集したモジュールのみを更新する Hot Module Replacement という仕組みが使える(後述)
ということでwebpack-dev-server
をnpmでいれる。
$ npm install --save-dev webpack-dev-server
そしてconfigも作る。
const path = require('path'); const entry = require('./entry'); module.exports = { entry: entry, output: { path: path.resolve(__dirname, '../dist'), filename: '[name].js', publicPath: "/js/" }, devtool: 'source-map', devServer: { contentBase: path.resolve(__dirname, '../pages'), port: 3000 }, resolve: { extensions: ['.js', '.jsx'] }, module: { rules: [ { test: /\.jsx*$/, exclude: /node_modules/, loader: 'babel-loader' } ] } };
後の話だけど、これを使ったらjsが任意の場所(ここでは/dist
以下)に生成されないなあと思っていたけれど、webpackの設定にoutput.publicPathを指定したらそこにjsが生成されたことになってちゃんと読み込まれた。
でもこれはwebpack-dev-serverでjsにaccessするためのrouting設定であって、file自体がどこにあるのかはまだよくわかってない。というか見当たらないんだけど。どこに生成されたし。もしかしてされないのかな?
と悩むこと一時間ほど。
webpack-dev-server | Webpack 1.13.0 日本語リファレンス | js STUDIO
上記のwebpack-dev-serverの設定を使用すると、buildフォルダの静的ファイルがWebサーバーのファイルとして提供されます。 これはソース・ファイルの変更の監視を行い、変更があればバンドルを再コンパイルします。
この再コンパイルされたバンドルは、publicPathで指定された相対パスのメモリーから提供され、 あなたが設定した出力先のディレクトリに書き込まれません。 同じURLのパスに既にバンドルが存在している場合、メモリ内のバンドルが優先されます。(デフォルト)
仕様かよ! と叫んだ僕がいる。
commandの登録
さてnpm run xxx
でcompileしたりできるようにpackage.jsonに以下を加える。
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config config/webpack-production.config.js --progress", "build:debug": "webpack --config config/webpack.config.js --progress", "start": "webpack-dev-server --config config/webpack-dev.config.js --watch --progress --inline" },
ここに定義されたものが実行できる。npm run build
すればbuildされるし、npm run start
すれば、webpack-dev-server
が起動して、port 3000でserverが起動しつつconfig/webpack-dev.config.js
のentryに定義されたfileやそれがimportしているfileが更新されれば自動更新されるようになる。
scriptsの中で呼び出すcommandは./node_modules/.bin/
以下のcommandをそのまま扱えるので、npmでinstallしたcommandはここで呼び出すと楽に実行できる。
おわりに
ここまでで環境構築は終わり。……普通にまとまってしまった。というか今回は終わってから書いているからややちぐはぐな感じがする。やっぱりやりながら書こう。
次回は/src
と/pages
以下のfileの作り方、つまりreact関連のfileを以下を参考に実際に作っていく。