【React】Reactに爆速で入門する4。Webpackを使ってバンドルする

はじめに

Reactに爆速で入門する1~3までで、React自体に対する理解はできてきました。(第1回はこちら)
しかし、入門1~3では、1つのファイルで、しかもReactやBabelの読み込みをscriptタグでCDNで行っていました。

実際のフロントエンド開発においては、この方式はまず採用されていなくて、大体はWebpackやBrowserifyやらを使用して、トランスパイル&バンドルするのが一般的ですよね。

ということで、今回は現状最も人気があると思われる(主観)Webpackを使用して、第3回までに作ったファイルを分割して見やすく、その分割したファイルをトランスパイル&バンドルするということをやっていこうと思います。

ベースは第3回のリスト表示するコードになりますので、コードを取得していない方はもってきてください。

また、npmを使用するので、とりあえずnodeをインストールしてください。公式ページからDLできます。

必要なプラグインをインストールする

とりあえず、npm initします。

 

npm init -y

次に、ReactとWebpackとBabelをインストールします。

npm install --save react react-dom
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react webpack

 

結果のpackage.jsonは下記のような感じになっていると思います。(webpack-dev-serverは除く)

{
  "name": "react-minimal",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "production": "webpack -p",
    "dev": "webpack -d",
    "watch": "webpack -d --watch",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^16.2.0",
    "react-dom": "^16.2.0"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "babel-preset-react-hmre": "^1.1.1",
    "webpack": "^3.9.1",
    "webpack-dev-server": "^2.9.5"
  }
}

WebpackとBabelの設定ファイルを作る

さて、使用する色々なもののインストールが完了したら、webpackとbabelの設定フィルをつくります。

webpackの設定ファイル

webpack.config.jsとしてルートに配置します。とりあえずはコピペでokです。

const path = require('path');

module.exports = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    js: './main.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.[name]',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['react', 'es2015']
        },
      }
    ]
  },
  plugins: [
  ],
  devServer: {
    contentBase: './dist/',
    port: 8080,
    inline: true,
    historyApiFallback: true,
    clientLogLevel: "info",
    stats: { colors: true }
  }
};

srcフォルダのmain.jsを起点としてjsファイルを探していき、出力はdistフォルダ以下にbundle.jsとしてね、的な感じのことを書いてあります。

babelの設定ファイル

.babelrcとしてルートに配置します。

{
"presets": ["es2015", "react"]
}

 

前回作成したファイルを分割する

第3回で作成したindex.htmlをコンポーネントごとに分割していきましょう。
index.htmlは、/dist/index.htmlとして配置し、それ以外のファイルは/src/以下に配置してください。

/dist/index.html

まずはindex.htmlを。もともとこのファイルに全てがつめこまれていましたが、下記の通りwebpackから生成されるbundle.jsを読み込む部分1つ残して、残り全てはクリアしてしまいます。

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

  <script src="./bundle.js"></script>
</body>
</html>

/src/main.js

エントリポイントとしてwebpack.config.jsに記述したmain.jsを作ります。
第3回のindex.htmlの、class定義部以外の全てをここにコピーする感じです。

また、React, ReactDOMのimportを忘れずに、また、ListAppをRenderしますのでListAppのimportも行います。

ListAppやMyListコンポーネントは、/src/components/に配置する予定ですのでListAppの読み込み先は./components/ListAppにします。

import React from 'react';
import ReactDOM from 'react-dom';
import ListApp from "./components/ListApp";

var my_items = [
    {
      name: "hoge",
      text: "this is hoge text"
    },
    {
      name: "foo",
      text: "I'm foo"
    },
    {
      name: "bar",
      text: "this is bar"
    }
  ];

ReactDOM.render(
  <ListApp items={my_items} />,
  document.getElementById("app")
);

/src/components/ListApp.js

ListAppもmain.jsと同じで、React関連の読み込みとMyListの読み込みをします。
main.jsから参照されますので、export defaultでclass自体をexportするのを忘れずに。

import React from 'react';
import ReactDOM from 'react-dom';
import MyList from "./MyList"

export default class ListApp extends React.Component {

    constructor(props){
      super(props);

      this.state = {
        items: props.items
      };
    }

    render(){
      return(
        <div>
          <h3>List App</h3>
          <div>
            <input id="ListApp-name" placeholder="name" />
            <input id="ListApp-text" placeholder="text" />
            <button onClick={this.handleClick.bind(this)}>add</button>
            <MyList items={this.state.items} />
          </div>
        </div>
      )
    }

    handleClick(event){
      this.setState({
        items: this.state.items.concat({
          name: document.getElementById("ListApp-name").value,
          text: document.getElementById("ListApp-text").value
        })
      });
    }
  }

 /src/components/MyList.js

MyList.jsは特筆すべきところはないですね。他と同じです。

import React from 'react';
import ReactDOM from 'react-dom';

export default class MyList extends React.Component{
    constructor(props){
      super(props);
    }

    render(){
      return(
        <ul>
          {
            this.props.items.map((v, i)=> {
              return (
                <li key={i}>
                  <p>name:{v.name}</p>
                  <p>text:{v.text}</p>
                </li>
              )
            })
          }
        </ul>
      )
    }
  }

これでファイル分割の準備が完了しました。

ここまでで、ファイル構成は下記のようになっているはずです。

  • /
    • /src
      • /components
        • ListApp.js
        • MyList.js
      • main.js
    • /dist
      • index.html
    • package.json
    • webpack.config.js
    • .babelrc

ビルドする

ビルド用に、下記のコマンドをscriptsに追記します。

  "scripts": {<span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span>
      "production": "webpack -p",
    "dev": "webpack -d",
    "watch": "webpack -d --watch",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

それでは、webpackによるトランスパイル&バンドルの動作確認します。

ルートで下記のコマンドを叩きます。

npm run dev

成功すれば、/dist/bundle.jsが生成されているはずです。

そして、/dist/index.htmlを開くと下記のように第3回と同じものが表示されます。

これでやっとモダンなフロントエンド開発に近づいてきました。

今回は以上です!

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です