Skip to content

Instantly share code, notes, and snippets.

@Altech
Created April 23, 2021 02:14
Show Gist options
  • Save Altech/ee599530e69870f1533d15ce023be34d to your computer and use it in GitHub Desktop.
Save Altech/ee599530e69870f1533d15ce023be34d to your computer and use it in GitHub Desktop.
React Study

React

create-react-app (typescript mode)

立ち上げ

  • `yarn start` で立ち上げると、ブラウザで React アプリケーションが開く
  • ファイルを編集すると即座にブラウザの表示も反映された(すごい)
  • 試しにプログラムエラーを起こしてみたら、ターミナルの方にエラーが表示された
  • ちょっと色々良い感じにやってくれすぎて何が起こっているのかわからないので仕組みの理解を進める

`yarn start`

  • package.json の scripts セクションにコマンドが定義されている(つまりここをまず見るとどういうコマンドが用意されているかがわかる)
  • この場合は、`start` コマンドが `react-scripts start` と言う別のコマンド的なものにマッピングされている
  • `yarn react-scripts start` でも同じように立ち上がることを確認した

react-scripts の本処理の実行に至るまで

  • 依存ライブラリは node_modules 以下ににインストールされている
  • react-scripts に移動
  • start で grep(ag) すると、`/bin/react-scripts.js` がヒット。どうやら、この実行ファイルにそのままコマンドライン引数が渡っている模様
  • 疑問:yarn にこういった実行ファイルを expose する仕組みはあるのか?それとも、bin 以下に置いたら勝手に expose される規約があるのか?
    • 答え:react-scripts の package.json を見ると、bin セクションに `react-scripts` を `./bin/react-scripts.js` にマッピングすると言うことが書かれている
    • したがって、ここに書けば任意の js ファイルを実行ファイルとして expose することが可能で、bin ディレクトリ以下に置くのは慣習的なものであることがわかる
  • 戻って:`/bin/react-scripts.js` は受け取った <command> を `./scripts/<command>` にそのまま委譲する
  • まとめ:`yarn start` は react-scripts の `./scripts/start.js` を呼び出す

react-scripts#start

  • 180行程度の短いファイルだが、起動処理の全てが書かれている
  • やっていること:WebpackDevServer の起動
    • `const devServer = new WebpackDevServer(compiler, serverConfig);`
    • `devServer.listen(port, HOST, ..)`
  • コンパイラ:webpack 系のユーティリティライブラリにまとめられている
    • `createCompiler({appName, config, devSocket, urls, useYarn, useTypeScript, tscCompileOnError, webpack });`
    • 与えている入力は、おおよそどう言うことをコンパイラがになっているのかを想像する上で参考になる

build してみる

  • yarn start した JS を見てみるも、あらゆるモジュール参照に webpack という文字列が出てくる
  • 開発中だとホットモジュール・リプレイスメントとか色々有効になっていて動作理解が難しくなるかもなのでとりあえず build してみる
  • `yarn build` で build ディレクトリに成果物を生成して、`serve -s build` でサーバーを立ち上げる
  • mainなんちゃら.js にアプリケーションコードが入っていることがわかるが、`yarn start` していた開発環境と違って minimize されていて読めない

minimize のプロパティを発見してオフにする

  • react-script の build コマンドを追っていくと、build 時に config が渡っている。この辺りで成果物がコントロールできそう。
  • react-script に webpack.config.js と言うファイルがあったのでそれっぽい。
    • 読んでいくと minimize と言うプロパティがあり、これが Production 環境かどうかで分岐していることがわかる。それっぽい。
    • (この node_modules 以下のコードを書き換えたら直ちに反映されるのか?)
  • webpack.config.js の minimize を常に false になるようにしてビルド・サーブしてみると、変数名などが置き換えられていない JS を生成することができた
    • ただ、モジュール名などは実行時にも webpack が管理しているらしい。例えば、`ReactDOM.render` は次のように置き換えられている。
      • `var react_dom_default = __webpack_require__.n(react_dom);`
      • `react_dom_default.a.render` (a は遅延ロードのためのフックか何かだろうか・・)
    • とまれ、モジュールの管理や読み込みのところでランタイムでも Webpack が噛んでいるということがわかった
  • ついでにコードがロードされる仕組みを追っていきたいが、ちょっと難読化されていてむずいため必要になったら帰ってくることにする

minimize されたものとそうでないものを見比べる

  • create-react-app は実際に開発をするには便利だと思うけど、色々最適化やツールチェインが入りすぎていて、学習が難しいと思った。
  • そういうのじゃないシンプルな React 入門を探す。
  • 正真正銘のReactだけの不純物なしでReact入門 これが良さそう。
  • 今回は TypeScript も使いたいので、これを TypeScript でやっていけるようにしよう。
  • とりあえず TypeScript が JavaScript にコンパイルできてブラウザから読み込めればそれで良いはず!なのでそれをできるようにする。
  • 必要だと予想されるもの:
    • React のような外部ライブラリを組み合わせる
    • TSX -> TS (JSX -> JS ?) のコンパイル
    • TypeScript -> JavaScrit のコンパイル

create-react-app を使わないでセットアップ

TypeScript のプロジェクトを作る

  • これはプロトタイプと学習を目的としているので、パフォーマンスの最適化や、互換性の対応を抜きにして、プレーンな JS / TS で原理的な理解を深めたい
  • 前提、JS にもモジュール機能はあるし、TS はコンパイルすれば良いだけだから、シンプルに開発できるはず(JSX だけはどうなるんだろう)
  • どうやるか?とりあえず TS のコンパイルができるプロジェクトを作る必要がありそう。 - TypeScript + Node.js プロジェクトのはじめかた2020 - Qiita
  • `yarn init` でプロジェクト管理を開始できる
    • yarn.lock : バージョンロック
    • package.json:プロジェクト全体の設定的なもの
  • `tsc –init` で TypeScript の管理を開始できる
    • tsconfig.json:TypeScript 専用の設定的なもの
      • target:ここでトランスパイル対象の言語を指定できる。es5 になってるけど、ここでは ES2019 とかにしておく。
  • ここまでで通常のプログラムのコンパイルはできるようになった
  • yarn tsc でプロジェクトのファイルをコンパイル。
    • ついでに、TSファイルは src 以下、出力ファイルは dist 以下に置かれるように調整する。

React JSX を含めてファイルをコンパイル

  • create-react-app と同じコードをこちらに持ってくる
  • そうすると JSX(TSX) が当然ながらパースできない。
  • TSX の扱いを少し調べると、コンパイラのサポートがあることがわかった。
    • React JSX with TypeScript(1.6) - Qiita
    • 確かに、tsconfig.json にも jsx オプションがコメントアウトされた形で入っている。これを “react” に書き換える。
  • React がなくて import error なので `yarn add react` する
  • すると今度は型エラーが出るようになった。ある意味で順調。
    • any 型だよっていう怒られ方をしている。
    • `npm i –save-dev @types/react` を試してね、と出る。親切だ。。
      • @types は TypeScript の型付けプロジェクト的なやつ。
    • yarnチートシート - Qiita で翻訳。`yarn add –dev` すれば良い。
      • –dev したものは package.json の deeDependencies に追加される。
  • yarn tsc で何も言われずコンパイルが通った!

ESモジュール前提でコンパイルする

  • 早速 TSX ファイルのコンパイル結果を見にいく。かなりそれっぽいファイルが出来上がっている。
  • ただ、モジュールの読み込みの仕組みが、組み込みっぽくない冗長な記述になっている。不思議。。
  • `React.createElement` と書いたものがそのままコンパイルしても維持されて欲しいところが、`react_1.default.createElement` となっていたり。
  • 出力ソースでググると、CommonJS と言うものっぽいことがわかる。
  • つまり今はブラウザで動くアプリケーションを開発しようとしているので、CommonJS は使わなくていいはず。標準で行きたい。
  • tsconfig.json を見ると、target の真下に module というプロパティが確かにあり、これが commonjs になっている。これを ES2015 に変えれば良さそう。
  • module プロパティを書き換えて動かしてみると、完全に期待通りのコードが生成された!

ブラウザで動かす

  • ES Module / TSX で書いた TypeScript を ES Module の JavaScript にコンパイルするところまで成功した
  • そろそろブラウザで動かしたい!あれ、どうやるんだろう・・。
  • ここではローカルファイルの index.html をブラウザで開いて、依存ライブラリを vendor.js とかにテキトウにまとめて読み込んで、それ以外をそのまま読み込む想定でいる
  • vendro.js にまとめるのに Webpack ? とはいえホットモジュールローディングとかそういう複雑なことはひとまずやってほしくなくて、単に配信したいだけなんだよなあ。
  • しばらく調べる。うーん、ちょっとやりたいことに対して過剰感。
  • ひとまず問題を分離しよう。まず、React や ReactDOM は、最悪ここでは unpkg.com から読み込める。となると、自分が書いたアプリケーションコードを読み込めば、全体として動くはず。
  • ここで、アプリケーションコードは ES Moudle を使っている。ES Moudle ってブラウザでそもそも動くんだっけ?
  • ブラウザで覚えるES Modules入門 - JavaScriptでモジュールを使う時代 - ICS MEDIA
  • ここで、ローカルファイルをそのままブラウザで開いて動かそうとする。
    • どうやらら script を type=”module” として読み込もうとすると、MIME type を厳格にチェックするようだ
    • そして、ローカルから読み込んだ JavaScript ファイルの MIME は空だと怒られ、試行錯誤。。
  • よく考えたら、ブラウザは HTTP で動くものなのだから、ローカルのファイルのようなサーバーが立ってないものを開いた時の挙動ってどこにも定義されていない危ういものなのでは?そう考えると色々辻褄が合う。。
    • **ローカルのもっとも単純な開発でもサーバーから配信する** という学びを得た。
  • さて、ここでやりたいことは「単純にディレクトリに置かれたファイルを HTTP 経由で配信する」ということである
    • ここではやはり Webpack のようなものは冗長なので使いたくない
    • 単純に配信したいだけなんだ・・そういうツールないかな・・。
    • そういえば、create-react-app で serve って npm があった。これ欲しいやつじゃない?
  • 試しに、serve でこれまで作っていたローカルファイルがあるディレクトリを配信してみる。MIME の問題もなく、別の ES Module で定義された関数を経由してダイアログアラートを表示することに成功した!
    • cURL を叩いてみても、ちゃんとヘッダーが入っている。
    • つまり serve コマンドというのはファイルの拡張子やら何やらみて良しなに HTTP で返してくれるえらいやつなんだなあ。
  • よし、ようやく React の DOM をレンダリングしていくぞ・・。
  • app.js で定義した App 関数をモジュール経由で entry.js から読み込んで、レンダリングすることに成功!
  • ここまで全部 JavaScript でやってしまったので TypeScript で動かす方に戻していく。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment