Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

2018年のReact最小構成の構築(非SPA対応)

スポンサードリンク

f:id:fa11enprince:20180815055023j:plain

今回作成する構成

  • React v16
  • Babel Core v6
  • Webpack v4

Reactの勉強を再開しようと思い、だいぶ前回から期間が開いてしまい、
最近のReact環境どうなんだってのがさっぱりわからず、
また、割とブラックボックスなcreate-react-appを使ってReact勉強しても初心者にあまりよくないのかも…
と思い、見よう見まねでReactの最小構成を作ってみました。
もちろんReduxは使ってません。
どうしてこういう構成にしたかというと、おそらくですが、まだフルでSPAを作るケースってそれほどないと思うのですよね。 ということでたぶん、既存のサーバサードでレンダリングされた各ページに動きをちょい足しするような使い方が多いと思いますので、 bundleも分割できるような作りにしました。
ひょっとするとこの辺りの用途はもうVue.js一択なのかもしれませんが、
そこそこ歴史があり、Reactには結構便利なコンポーネントが転がってるのでそこはVue.jsより優位性があるのではないのかと。

フロントエンド回りはだいぶ落ち着いてきた感があります。
Reactの勉強をするつもりが今回はほぼWebpack4の勉強になってしまいました…まぁいいか。
自分は最近Angularばかり触っていてReactにはかなり疎いので、
間違いやこうしたほうが良い等ありましたらご意見いただけると幸いです。
Angularはこのあたりほぼ何も考えなくてよいので楽ですね。

事前準備

開発環境を整えます。VS Codeがないとフロントエンドは始まりません。

VS Codeを入れる

これがないと始まりません
DLしてインストールしましょう
https://code.visualstudio.com/
Atomでもいいですが、AtomはVS Codeに完敗してしまった感があります

入れたほうが良いプラグイン

  • vscode-styled-jsx
  • Beautify
  • ESLint

Node.jsを入れる

DLしてインストール https://nodejs.org/ja/

webpackコマンドを叩けるようにする

$ npm install -g webpack

Reactプロジェクトを作成する

$ mkdir react-boilerplate

packageを追加する

$ cd $_
$ npm init -y
$ npm install --save react react-dom
$ npm install --save-dev webpack babel-loader babel-core babel-preset-react babel-preset-env babel-preset-stage-2 webpack-cli webpack-dev-serve

ソースコードを書く

ここを参考に階層を作成してください
若干フォルダ構成がSpring-Bootなのはご容赦ください。 下記にあるように src/main/client配下がReactのjsxを作成する領域
src/main/resources配下のpublicとstaticがwebサーバで公開する領域
として作成します。 今回は
index.htmlと
subpage/index.htmlのReactを用いたページを作成します。

src配下

src
└── main
    ├── client
    │   ├── components
    │   │   └── hello.jsx
    │   ├── index.jsx
    │   └── subpage
    │       ├── components
    │       │   └── world.jsx
    │       └── index.jsx
    └── resources
        ├── public
        │   ├── index.html
        │   └── subpage
        │       └── index.html
        └── static
            └── react
                この配下にトランスパイルされたjsファイルが生成される

 
src/main/resources/public/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>React Test</title>
    </head>
    <body>
        <div id="app" />
        <!-- NOTE: webpack-dev-server root path is src/main/resources -->
        <script src="/static/react/index.js"></script>
    </body>
</html>

 
src/main/resources/public/subpage/index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>React Subpage Test</title>
    </head>
    <body>
        <div id="app" />
        <!-- NOTE: webpack-dev-server root path is src/main/resources -->
        <script src="/static/react/subpage/index.js"></script>
    </body>
</html>

 
src/main/client/components/hello.jsx

import React, { Component } from 'react';

export default class Hello extends Component {
    render() {
        return (
            <h1>Hello React World!</h1>
        );
    }
}

 
src/main/client/index.jsx

import React from 'react';
import { render } from 'react-dom';

import Hello from './components/hello';

render(
    <div>
        <Hello />
    </div>,
    document.getElementById('app')
);

 
src/main/client/subpage/components/world.jsx

import React, { Component } from 'react';

export default class World extends Component {
    render() {
        return (
            <h1>Subpage Hello React World!</h1>
        );
    }
}

 
src/main/client/subpage/index.jsx

import React from 'react';
import { render } from 'react-dom';

import World from './components/world';

render(
    <div>
        <World />
    </div>,
    document.getElementById('app')
);

 
注意
import { Hoge } from './components/hoge';

import Hoge from './components/hoge';
は意味が違うので注意
Angularのときここは意識していなかった・・・
defaultがついたクラスだと後者でimportしないと
Warning: React.createElement: type is invalid -- expected a string
という警告がでて、エラーになる。

package.jsonに追記する

$ vi package.json

...
  "description": "",
  "scripts": {
    "start": "webpack-dev-server --mode development --progress --colors --hot --open",
    "watch": "webpack --mode development --watch --progress --colors --hot",
    "build-dev": "webpack --mode development --progress --colors --hot",
    "build": "webpack --mode production --progress --colors"
  },
...
  "license": "MIT",
  "babel": {
    "presets": [
      "env",
      "react",
      "stage-2"
    ]
  },
  "dependencies": {
...

webpack.config.jsを書く

$ touch webpack.config.js
$ vi webpack.config.js
const path = require('path');

module.exports = {
    entry: {
        'index': path.resolve(__dirname, 'src/main/client/index.jsx'),
        'subpage/index': path.resolve(__dirname, 'src/main/client/subpage/index.jsx'),
    },
    output: {
        path: path.resolve(__dirname, 'src/main/resources/static/react'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: 'babel-loader',
            }
        ]
    },
    resolve: {
        extensions: ['*', '.js', '.jsx'],
    },
    devServer: {
        contentBase: path.resolve(__dirname, 'src/main/resources'),
        port: 3000,
    },
}

※Macだと、バックスラッシュと円マークが違うので注意!
macだとbackslashと円マークは別物なので注意
Optionキー+円マーク
ちなみに間違えると・・・
"You may need an appropriate loader to handle this file type”
のエラーが出続けてハマった・・・
Macではバックスラッシュ()と円マーク(¥)を区別するのを忘れていて、
正規表現の部分を円マーク(¥)で書いていた・・・・・・

resolveのextentionsを書くことによりjsxでimport時に拡張子を省略できる

path.resolve, path.join, __dirnameを知らない人はググることをおすすめ(私もよく知らなかったのでググりました)。
ざっくりいうと__dirnameは現在動かしているカレントのディレクトリ的なものです。
resolve, joinは動きが違いますが、パスをconcatする的なものです。

Reactの動作確認をする

まずはビルドする

$ npm run watch

別ウィンドウで

$ npm run start

これでwebpack-dev-serverが立ち上がります。 http://localhost:3000/public/
http://localhost:3000/public/subpage/
をたたくと、作ったものが表示されます。

プロダクション用のビルドはnpm run buildで!
以上です!!

完成したものはここです。

参考リスト

minimai-react-webpack-babel-setup
React simple boilerplate

その他関連情報

webpack tutorial
react-webpack-babel tutorial
webpack-dev-serverの使い方
npm option
webpackでbundle分割

旧バージョンの情報

※情報が古いのでwebpackの記載方法が非推奨になっているものもあります。
Webpack + React + ES6の最小構成を考えてみる。
Reactを「webpack + babel-loader」でビルドする方法