サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

React入門 06: ローカルサーバーの立ち上げとJSXのコンパイル


このReact入門シリーズでは、Reactのチュートリアルのサンプルファイルでローカルサーバーを立ち上げました(「React入門 03: データはJSON形式にしてページをローカルサーバーで表示する」03「チュートリアルのサンプルファイルでサーバーを立てる」)。また、JSXのコンパイルはbabel-coreのJavaScriptライブラリを用いて、ブラウザ上で行っています(「React: まずは動かしてみる」04「BabelでJSXをコンパイルする」)。けれど、チュートリアルの設定にとらわれずに、ローカルサーバーを動かしたいこともあるでしょう。また、コンテンツをサーバーに公開するときは、ユーザーのブラウザ上で標準のJavaScriptコードに変換するのでなく、あらかじめ(静的に)コンパイルしておく方がブラウザの負荷が下げられます。そこで、ローカルサーバーの立ち上げとJSXのコンパイルについて、簡単なやり方をご紹介します。

なお、Node.jsがインストールされていなければならず(「React入門 03: データはJSON形式にしてページをローカルサーバーで表示する」02「チュートリアルのサンプルファイルとNode.jsのインストール)、後述のBabelを動かすにはバージョンは6.3.1でなく4.4.7 LTSをお使いください。

01 http-serverでローカルサーバーを立ち上げる

ローカルサーバーはhttp-serverで立ち上げることにします。コマンドラインツール(OS Xならターミナル、WindowsはNode.js command promptなど)を開いて、npmのコマンドで、つぎのようにグローバルにインストールします。


npm install http-server -g

そして、コマンドラインツールでローカルサーバーのルートにするディレクトリに切り替えます。あとは、つぎのようにコマンドを打ち込むだけです(コマンドに使えるオプションについては「Node.jsのhttp-serverっていうコマンドラインのウェブサーバーが便利」参照)。http://localhost:8080/でルートが開きます。


http-server

試しに、「React入門 05: フォームからサーバーにデータを送ってページに加える」コード002「フォームに入力したデータをサーバーに送ってページに加える」をhttp-serverで動かしてみましょう。チュートリアルのserver.jsは使いませんので、ルートのフォルダ名は「public」でなくても構いません(ここでは変えずにおきます)。そして、データを読み書きするJSONファイル(comments.json)は、ルートにおいてください(図001)。

図001■サーバーのルートにcomments.jsonを納める

図001

前出コード002は、server.jsにもとづいてプロパティ(url)に定めたJSONファイルのパスを、つぎのように(comments.jsonに)改めます。コマンドラインツールでディレクトリをルートにするフォルダ(public)に移し、コマンドhttp-serverを打てばルートのHTMLドキュメント(http://localhost:8080/sample.html)が開けます(図002)。


ReactDOM.render(
	<CommentBox url="comments.json" />,
	document.getElementById('content')
);

図002■JSONファイルのデータが表示されてフォームから加えられる

図002

02 BebelのReact presetでJSXを標準のJavaScriptコードにコンパイルする

ReactのJSXをコンパイルするために、BebelのReact presetを使うことにします。コマンドラインツールでReactのディレクトリに移り、つぎのnpmのコマンドでbabel-cliとbabel-preset-reactをインストールします。


npm install --save-dev babel-cli babel-preset-react

package.jsonファイルの"devDependencies"には、ふたつのモジュールが加わります。そのあとに続けて、つぎのように手書きで"babel"を加え、"presets"を定めます。


{

  "devDependencies": {
    "babel-cli": "^6.14.0",
    "babel-preset-react": "^6.11.1",

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

package.jsonファイルにはもうひとつ、npmから呼び出すコマンドを"scripts"に加えます。コマンドbabelのあとに変換もとのディレクトリ、オプション-dに続けて変換先ディレクトリを与えます。

babel 変換もとディレクトリ -d 変換先ディレクトリ

つぎの記述はReactの練習用ディレクトリから、publicフォルダ内のsrcにあるJSXのJavaScriptファイルを、同じフォルダのlibにコンパイルして納めることになります。


{

  "scripts": {

    "build": "babel public/src -d public/lib"
  },

 }

コマンドラインツールから、npmでつぎのようにスクリプト(build)を呼び出します。変換先フォルダ(public/lib)に、標準のJavaScriptコードにコンパイルされたファイルができ上がります。


npm run build

ご参考までに、前出「React入門 05: フォームからサーバーにデータを送ってページに加える」コード002「フォームに入力したデータをサーバーに送ってページに加える」からコンパイルしたJavaScriptの記述を以下のコード001に掲げました。JSXのタグがReact.createElement()メソッドに書き替えられています。

このJavaScriptファイル(script.js)を読み込むHTMLドキュメント(sample.html)には、つぎのように手を加えます。まず、babel-coreのJavaScriptライブラリ(browser.js)は要りません。つぎに、<script>要素のsrc属性には、コンパイルした(フォルダlibの)ファイルのパスを与えます。<body>要素の最後に移したのは、ReactDOM.render()メソッドの第2引数に渡した親要素(id属性"content")が読み込まれるのを待つためです。<script>要素を<head>要素に入れたい場合には、ドキュメントがロードされてからJavaScriptコードの処理を行うように書き直さなければなりません。


<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample</title>
<script src="https://npmcdn.com/react@15.3.0/dist/react.min.js"></script>
<script src="https://npmcdn.com/react-dom@15.3.0/dist/react-dom.min.js"></script>
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.6.2/remarkable.min.js"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<!--<script src="src/script.js" type="text/babel"></script>-->
</head>
<body>
	<div id="content"></div>
	<script src="lib/script.js" type="text/babel"></script>
</body>
</html>

コード001■標準のJavaScriptコードにコンパイルされたファイルの記述


var CommentBox = React.createClass({
	displayName: 'CommentBox',

	getInitialState: function () {
		return { data: [] };
	},
	componentDidMount: function () {
		$.ajax({
			url: this.props.url,
			dataType: 'json',
			cache: false,
			success: function (data) {
				this.setState({ data: data });
			}.bind(this),
			error: function (xhr, status, error) {
				console.error(this.props.url, status, error.toString());
			}.bind(this)
		});
	},
	handleCommentSubmit: function (comment) {
		$.ajax({
			url: this.props.url,
			dataType: 'json',
			type: 'POST',
			data: comment,
			success: function (data) {
				this.setState({ data: data });
			}.bind(this),
			error: function (xhr, status, error) {
				console.error(this.props.url, status, error.toString());
			}.bind(this)
		});
	},
	render: function () {
		return React.createElement(
			'div',
			{ className: 'commentBox' },
			React.createElement(
				'h1',
				null,
				'コメント'
			),
			React.createElement(CommentList, { data: this.state.data }),
			React.createElement(CommentForm, { onCommentSubmit: this.handleCommentSubmit })
		);
	}
});
var CommentList = React.createClass({
	displayName: 'CommentList',

	render: function () {
		var commentNodes = this.props.data.map(function (comment) {
			return React.createElement(
				Comment,
				{ author: comment.author, key: comment.id },
				comment.text
			);
		});
		return React.createElement(
			'div',
			{ className: 'commentList' },
			commentNodes
		);
	}
});
var Comment = React.createClass({
	displayName: 'Comment',

	rawMarkup: function () {
		var markDown = new Remarkable();
		var rawMarkup = markDown.render(this.props.children.toString());
		return { __html: rawMarkup };
	},
	render: function () {
		return React.createElement(
			'div',
			{ className: 'comment' },
			React.createElement(
				'h2',
				{ className: 'commentAuthor' },
				this.props.author
			),
			React.createElement('span', { dangerouslySetInnerHTML: this.rawMarkup() })
		);
	}
});
var CommentForm = React.createClass({
	displayName: 'CommentForm',

	getInitialState: function () {
		return { author: '', text: '' };
	},
	handleChange: function (eventObject) {
		var state = {};
		state[eventObject.target.id] = eventObject.target.value;
		this.setState(state);
	},
	handleSubmit: function (eventObject) {
		eventObject.preventDefault();
		var author = this.state.author.trim();
		var text = this.state.text.trim();
		if (!author || !text) {
			return;
		}
		this.props.onCommentSubmit({ author: author, text: text });
		this.setState({ author: '', text: '' });
	},
	render: function () {
		return React.createElement(
			'form',
			{ className: 'commentForm', onSubmit: this.handleSubmit },
			React.createElement('input', {
				type: 'text',
				id: 'author',
				placeholder: '名前',
				value: this.state.author,
				onChange: this.handleChange
			}),
			React.createElement('input', {
				type: 'text',
				id: 'text',
				placeholder: 'コメントを入力',
				value: this.state.text,
				onChange: this.handleChange
			}),
			React.createElement('input', { type: 'submit', value: '送信' })
		);
	}
});
ReactDOM.render(React.createElement(CommentBox, { url: 'comments.json' }), document.getElementById('content'));

本稿では、すぐに試しやすいローカルサーバーの立ち上げとJSXのコンパイルのやり方をご紹介しました。実際にコンテンツをつくるようになったら、GulpやGruntなどのタスクランナーツールを使って、作業の効率化も考えるとよいでしょう。


作成者: 野中文雄
作成日: 2016年8月30日


Copyright © 2001-2016 Fumio Nonaka.  All rights reserved.