Express
docker
React
babel
webpack-dev-server

React の Helloworldを docker-compose を使って webpack + babel , express, webpack-dev-serverを試したメモ

More than 1 year has passed since last update.

React のチュートリアルをdockerを使って実行


Hello world

webpack + express で試してみる

ディレクトリ構成は以下

webpack                     # Reactのトランスパイルを行うビルドツール

- Dockerfile
- package.json # npmのモジュールを管理・スクリプトの設定
- webpack.config.babel.js # webpackの設定ファイル。babelを利用してes6で記述可
- .babelrc # babelによるトランスパイルの設定ファイル
express # サーバー
- Dockerfile
src
- app.js # webpackのエントリポイント。このファイルをトランスパイルする。
myapp
- views # ビューファイル。
- error.jade # express-generatorによって作成されたものを上書きする。
- index.jade # index.jadeがホームに表示されるhtmlファイルとなる。
- layout.jade # index.jadeの使用するテンプレート
dist # webpackによって生成されるファイル群
- .gitignore # gitの管理外とする
- bundle.js # webpackによって生成されるファイル
docker-compose.yml # Dockerの設定ファイル

サーバー。express-generatorを使った構成。

FROM node:7.1.0

WORKDIR /my_express
RUN npm init -y
RUN npm install express-generator -g
RUN express myapp
WORKDIR /my_express/myapp
RUN npm install body-parser --save
RUN npm install cookie-parser --save
RUN npm install debug --save
RUN npm install express@5.0.0-alpha.2 --save
RUN npm install jade --save
RUN npm install morgan --save
RUN npm install serve-favicon --save

ビルドツール。Reactをインストール。es6を使うための設定も一緒に。


install と iは同じ意味。

FROM node:7.1.0

WORKDIR /my_webpack
RUN npm init -y
RUN npm i --save-dev webpack@2.1.0-beta.26
RUN npm i --save babel-polyfill
RUN npm i --save-dev babel-core
RUN npm i --save-dev babel-loader
RUN npm i --save-dev babel-preset-es2015
RUN npm i --save-dev babel-preset-stage-0
RUN npm i --save-dev babel-preset-react
RUN npm i --save react
RUN npm i --save react-dom
CMD ["npm", "run", "build"]

重要なのは"scripts":{"build":"webpack"}。


package.jsonを設定したディレクトリでnpm installを行った後に

npm run build とすることでwebpackを利用してトランスパイルが可能。


package.json

{

"name": "my_webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC"
}

トランスパイルを行うbabelの設定。

{

"presets": [
"es2015",
"stage-0",
"react"
]
}


webpack.config.babel.js

import 'babel-polyfill';

module.exports = {
entry: './src/app.js', // app.jsをエントリポイントにしてトランスパイルする。
output: {
path: 'dist', // dist/フォルダにビルドしたファイルを出力する。
filename: 'bundle.js' // ビルドするファイルの名前
},
resolve: {
extensions: [".js", ".jsx"] // 省略できる拡張子。
}, // import h from 'hoge.js' を import h from 'hoge'のように書ける
module: {
loaders: [
{test: /\.jsx?$/, loaders: ['babel-loader']}, // .js、.jsxの拡張子のファイルにbabel-loaderを利用した
] // トランスパイルを行う
}
};



docker-compose.yml

server:

build: ./express
volumes:
- ./myapp/views:/my_express/myapp/views
- ./dist:/my_express/myapp/public/javascripts
ports:
- "80:3000"
command: [node, bin/www]

webpack:
build: ./webpack
volumes:
- ./src:/my_webpack/src
- ./dist:/my_webpack/dist
- ./webpack/package.json:/my_webpack/package.json
- ./webpack/webpack.config.babel.js:/my_webpack/webpack.config.babel.js
- ./webpack/.babelrc:/my_webpack/.babelrc
command: [npm, run, build]


Reactを使ったjsファイル。


app.js

import React from 'react';

import ReactDOM from 'react-dom';

ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('root')
);


トランスパイルをしたjsファイルを読み込むhtmlテンプレート。


ポイントは最後にjsファイルを読み込むこと。


headで読み込んだりするとまだdomが出来ていないので実行がうまくいかない。


index.jade

extends layout

block content
p Welcome to #{title}
div#root
script(src='/javascripts/bundle.js')


dockerの設定ファイル。サーバーとビルドツール両方の設定を行う。


docker-compose.yml

server:

build: ./express
volumes:
- ./myapp/views:/my_express/myapp/views
- ./dist:/my_express/myapp/public/javascripts
ports:
- "80:3000"
command: [node, bin/www]

buildtool:
build: ./webpack
volumes:
- ./src:/my_webpack/src
- ./dist:/my_webpack/dist
- ./webpack/package.json:/my_webpack/package.json
- ./webpack/webpack.config.babel.js:/my_webpack/webpack.config.babel.js
- ./webpack/.babelrc:/my_webpack/.babelrc
command: [npm, run, build]


$ docker-compose build

$ docker-compose up

ブラウザでアクセスするとHello worldが表示される。


webpack-dev-serverの使用

ホストを0.0.0.0とする設定に気が付かずに、サーバを起動しても繋がらないと2時間くらい悩んだ。


webpack.config.babel.js

import 'babel-polyfill';

// コンテナ中では/my_webpack/webpack.config.babel.jsに配置
import HtmlWebpackPlugin from 'html-webpack-plugin';

module.exports = {
entry: './src/app.js',
output: {
path: 'dist',
filename: 'bundle.js'
},
resolve: {
extensions: [".js", ".jsx"]
},
module: {
loaders: [
{ test: /\.jsx?$/, loaders: ['babel-loader']}
]
},
// HTMLファイル作成プラグインを追加
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
})
],
// ブラウザの開発ツールでソースマップを確認できるようにする
devtool: '#cheap-module-eval-source-map',
// webpack-dev-server用の設定
devServer: {
contentBase: './dist', // サーバーが見に行くディレクトリ
inline: true, // http:localhost:8080/webpack-dev-server/ではなくhtttp:localhost:8080/でアクセスできるようになる。
port: 8080, // ポート設定
host:"0.0.0.0" // ※ dockerのコンテナで立てたサーバが他のホストからアクセスできるように全てのネットワークインターフェースに接続
}
};


FROM node:7.1.0

WORKDIR /my_webpack
RUN npm init -y
RUN npm install --save-dev webpack@2.1.0-beta.26
RUN npm i --save babel-polyfill
RUN npm i --save-dev babel-core
RUN npm i --save-dev babel-loader
RUN npm i --save-dev babel-preset-es2015
RUN npm i --save-dev babel-preset-stage-0
RUN npm i --save-dev babel-preset-react
RUN npm i --save react
RUN npm i --save react-dom
RUN npm i --save-dev webpack-dev-server # 追加
RUN npm i --save-dev html-webpack-plugin # 追加


src/app.js

import React from 'react';

import ReactDOM from 'react-dom';

function formatName(user) {
return user.firstName + ' ' + user.lastName;
}

const user = {
firstName: 'Harper',
lastName: 'Perez'
};

const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);

ReactDOM.render(
element,
document.getElementById('root')
);



src/index.html

<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

startのスクリプトを追加


webpack/package.json

{

"name": "my_webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC"
}

最後のコマンドを[npm, run, start]に変更


docker-compose.yml

devserver:

build: ./webpack
volumes:
- ./src:/my_webpack/src
- ./dist:/my_webpack/dist
- ./webpack/package.json:/my_webpack/package.json
- ./webpack/webpack.config.babel.js:/my_webpack/webpack.config.babel.js
- ./webpack/.babelrc:/my_webpack/.babelrc
ports:
- "80:8080"
command: [npm, run, start]

$ docker-compose build

$ docker-compose up


webpack-dev-serverのhotloaderを有効にする。

ファイルの変更を検知して、モジュール単位で置き換える。


webpack/package.json

{

"name": "my_webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"start": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC"
}

babelのauto compactが有効になっていて警告がでているので消す設定もついでに行う。


Vagrantを使用しているため、ポーリングの設定をしないとファイルの変更が検知されない。

// webpack.config.babel.js

import 'babel-polyfill';
import path from 'path';
import webpack from 'webpack'; // hotmoduleを使用するために必要

module.exports = {
context: __dirname + '/src',
entry: {
javascript: './app.js',
html: './index.html'
},
output: {
path: 'dist',
filename: 'bundle.js'
},
resolve: {
extensions: ["", ".js", ".jsx"]
},
module: {
loaders: [
{ test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader?compact=false',
include: path.join(__dirname, 'src'),
},
{
test: /\.html$/,
loader: 'file-loader?name=[path][name].[ext]'
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin() // hot-loaderプラグイン

],
devServer: {
contentBase: './dist',
inline: true,
port: 8080,
host:"0.0.0.0",
hot: true, // hot-loaderの使用設定。
},
// vagrantの仕様で、pollingを使用しないとファイルの変更が検知できない。
watchOptions: {
aggregateTimeout: 300,
poll: 5000 // 5秒ごとにファイルの更新を確認
}
};


参考

react tutorial

Webpack + React + ES6の最小構成を考えてみる。

webpack-dev-serverの基本的な使い方とポイント

意訳

Stack over flow