はじめての React

Vue をちょっと使っていたのですが、解決できない問題にぶち当たってしまい、React にも挑戦してみた次第です。

Vue も React もデータバインディングを得意としたフレームワークとなんとなく捉えていますが、React のほうがより UI に近いという印象を持っています。

とはいっても、どちらも全然理解してないので、今回は React で簡単な SPA を作ってみようと思います。

環境構築

ちなみに、SPA (Single Page Application) とは単一ページで完結している Web アプリケーションです。私の理解があっていれば、フォーム入力などによるページ遷移も行えますが、このとき見た目上はページが切り替わるものの URL は変わりません。

余談ですが、SPA は URL が統一されているので、所謂 SEO 対策には不得手であると聞いたことがあります。最も SEO 対策がどこまで意味を持っているのかというのは謎ですが…。

SPA に対して SSR (Server Side Rendering) というのもあり、これは JS の実行によってブラウザ上で DOM を生成、操作していたところをサーバ側で描画してブラウザに送信するシステムをいいます。SPA と一緒に使うことでクローラーの解析に対応できるようです。正直わけわからん…。ここに AMP やら PWA (0Service Worker) やらの概念を突っ込まれてくると混乱の極みです。

閑話休題。

早速、SPA を開発するための環境を作っていきます。React も Vue のように HTML に <script> タグで埋め込んで使うことができるようですが、読み込みが遅くなるので、Node.js で動かすこと前提で進みます。

$ npm install -g yarn

最初に Yarn を入れておきます。これまで NPM を使っていましたが、Yarn のほうが速いらしく、NPM ユーザーの環境と衝突することもないようです。

# 任意のディレクトリを作成
$ mkdir hello-react
$ cd hello-react

# package.json 作成
$ yarn init -y

# webpack でのビルドに使うパッケージをインストール
$ yarn add --dev webpack babel-cli babel-core babel-loader babel-plugin-transform-react-jsx babel-preset-react react react-dom

yarn add --dev すると package.json に devDependencies として登録されます。--dev は開発中のビルドに必要なパッケージを指定するもので、本番環境では必要ないときに使うオプションです。

React で DOM をレンダリングするためには react, react-dom, babel-core が必要です。HTML に <script> タグで埋め込むときにはこれらの JS ファイルを読み込みます。

Hello React! してみる

JSX

React では JSX という記法が使われます。

<body>
  <div id="root"></div>
  <script type="text/babel">
    ReactDOM.render(
      <h1>Hello, world!</h1>,
      document.getElementById('root')
    )
  </script>
</body>

ReactDOM.render() 内でレンダリングされていますが、JS 内に DOM が含まれています。実際に DOM を生成するために、Babel の JSX トランスコンパイラ babel-plugin-transform-react-jsx を使います。

React コンポーネント

まず、ツリー構造は以下のようになります。

root
├ node_modules
├ src
│ ├ App.js
│ └ index.js
├ index.html
├ package.json
└ webpack.config.js

React コンポーネントは React.Component を継承して作成します。コンポーネント内部で DOM の振る舞いを記述し、外部からクラスを呼びだせるよう export default として render() メソッドにより DOM を返します。

import React from 'react'

export default class App extends React.Component {
    render () {
        return <h1>Hello, React!</h1>
    }
}

こうすると、呼び出した先で DOM として記述できます。

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(
    <App />,
    document.getElementById('root')
)

複数の JS ファイルは Webpack によってビルド時にバンドルすることで単一のファイルとして扱うことができるようになります。

そのファイルを index.html で読み込むように記述します。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <div id="root"></div>
  <script type='text/javascript' src="dist/bundle.js"></script>
</body>
</html>

ビルド

本体はできたので、ビルドの設定ファイルを記述します。

const path = require('path')

module.exports = {
  mode: 'development',
  entry: path.resolve(__dirname, '.src/js/index.js'),
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [{
      test: /\.js?$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        plugins: ["transform-react-jsx"]
      }
    }]
  }
}
$ node_modules/webpack/bin/webpack.js
Hash: ee193e1aaa39ed25d0a7
Version: webpack 4.16.5
Time: 2933ms
Built at: 2018-08-12 14:09:31
    Asset     Size  Chunks             Chunk Names
bundle.js  100 KiB       0  [emitted]  main
Entrypoint main = bundle.js
[14] ./index.js + 1 modules 361 bytes {0} [built]
     | ./index.js 168 bytes [built]
     | ./App.js 183 bytes [built]
    + 14 hidden modules

実行すると dist/bundle.js が生成され、index.html を開くと Hello, React! と表示されます。

設定ファイルの記述が間違っていると、下記のようなエラーが出ることがあります。

Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.

Webpack のバージョンによって設定ファイルの書き方が少し変わっているようですので、その際は公式ドキュメントを参考にしてください。ちなみに、私の環境は 4.6.15 です。

最後に

下記サイトが入門編として大変わかりやすかったです。

(参考)

React で作る今時の SPA (基本編) | Qiita