DRYな備忘録

Don't Repeat Yourself.

React+Babel+webpackの最小構成つくってだるかったことメモ

サンプルページ

3がつく数字でアホになるやつ

tl;dr

  • npm install -gはだるいので、npm scripts経由でwebpack呼ぶのがよさそう
  • webpackが扱うべきファイルによってloaderがひつよう(babel-loaderとか)
    • loaderを書く順番でひっかかって非常にだるかった
  • importの拡張子省略はwebpack.configで指定できるっぽい
  • ReactDOM.renderが評価される時点でrender targetがreadyじゃないといけないの

github.com

背景

  • なぜReactか
    • ちいさいjsの何かをつくるときは、gulpなどタスクランナーの設定が必要無いという点においてAngular(1系)使うのが好きだったんだけど、Angular2がComponentベースだというはなしを聞いて、じゃあええわべつにReact使うし、っていう気持ちになった
  • なぜBabelか
    • Componentベースで何かを作るとき、Componentごとにファイルを分割したほうが開発効率もよいだろうし、依存関係を明示するためにもES6(ES6ってもう言わない?)のimport使いたいし、React的にもclassextendsも使いたいし、でも実行環境はES5以下を想定するので6to5しましょう
  • なぜwebpackか
    • webpackはgruntやgulpなどのタスクランナーとは立ち位置がちょっと違くて、ES6のimportを解釈して必要な依存ファイルをかき集めてビルドしてくれるというのが決定的な特徴みたい。すべてのブラウザで共通のES6シンタックスが動くようになる()までは、ES6でJSのプロジェクトつくるときのスタンダードになっていく様相があるんではないか

エラー: Illegal import declaration

babel-loaderとjsx-loaderを使ってたので、jsxファイルの中でjsx記法とES6記法をつかっていても問題なくwebpackがビルドしてくれると思っていた。だけどどうやらbabel-loaderのあとに読み込んだjsx-loaderが、babel-loaderによって可能にしたimportの解釈を食ってた、みたいな挙動にでくわした。さらに、babel-loaderだけ読み込んでおけば、jsx->jsのコンパイルはしてくれるみたいだ、ということを知った。

Babel · The compiler for writing next generation JavaScript

JSX and React Babel has support for JSX and Flow.

stackoverflow.com

エラー: React is not defined

アプリのエントリポイントであるところのapp.jsでは、ReactDOMモジュールしか参照していないので、とうぜん、import ReactDom from 'react-dom'でじゅうぶんだと思っていた。

import ReactDOM from 'react-dom';
import Foo from './Foo';

ReactDOM.render(<Foo />, document.getElementById('main'));

しかしこれだと掲題のエラーが出る。きっとReactDOMモジュールの中でReactっていう名前を参照してるんだろうな、と思いつつも、webpackがimportを解決してくれるんじゃなかったっけって思った。でもそれは誤りで、node_modules以下にinstallされたreact-domっていうのはjs(ES5)で書かれているわけで、依存もimportを書いているわけではないので、webpackを通したところで依存が解決されたjsファイルがビルドされるわけではないんだろう。

+ import React from 'react';
import ReactDOM from 'react-dom';
import Foo from './Foo';

ReactDOM.render(<Foo />, document.getElementById('main'));

エラー: Target container is not a DOM element

これはべつにwebpackの構成の問題ではないんだけど、今回(というか毎回)ぶつかるエラー。とはいえなんも難しい話ではなく、rootなcomponentを最初にレンダリングするときに呼ばれる ReactDOM.renderの第二引数に指定されるrender targetが、まだreadyなDOMではないことに起因してるだけ。

ReactDOM.render(<App />, document.body);

なのでたとえば

setTimeout(function() {
  ReactDOM.render(<App />, document.body);
}, 0);

とするとか

<body onload="initApp()"></body>

とするとか、

<body>
  <section id="main"><!-- ターゲット --></section>
  <script src="app.js"></script>
</body>

とするとか、いろいろな解決方法が提案できる。

stackoverflow.com

参考

雑感

  • hjs-webpackを使えばらくちんという話を聞いたけど、何がなぜ必要でどう動いているのか理解するためには最小構成を自分でつくるという体験は大事だと思うんですよね

DRY

React Quickly

React Quickly