TypeScript+rollup.js+EmotionでReact開発できる環境を作ってみた
お久しぶりです。
前回のブログ投稿から、日が経ってしまいました。
ここ最近は個人的に作ってみたいサービスのイメージが湧いたので、AWS CDK(Python)で遊んでいたんですが、フロントエンドまでイメージが広がりました。
これまではVue.jsを使っていたんですが、折角なんで、きちんと触ったことのないReactでやってみようと思いつきました。
前回のブログ投稿からだいぶ日が経ってしまうとは、この時は思ってもいませんでした。
Motivation
Vue.jsにはVue CLIがあり、ReactにもCreate React Appというとても便利なCLIがあります。
今更ながらゼロからReact開発を学ぶので、CLIを利用しない環境構築をしてきちんと仕組みを理解してみようと思いました。
いっそのこと欲張りに全部盛りにしてやってみよう!!!(←ここが日が経った理由)
ということで
- TypeScriptを使ってみよう
- Babelとwebpackを使わないで実現してみよう
- 公式のライブラリを前提に組み上げてみよう
という前提条件を課して進めることをMotivationとしました。
Babelとwebpack以外にしてみようと考えた理由は以下の通りです。
ツール | 理由 |
---|---|
Babel | TypeScriptを利用するのでコンパイルはTypeScriptに任せたい |
webpack | 多機能なので軽量なバンドルの仕組みにしたい |
今回は勉強も兼ねているので、少しだけセオリーから外れた構成で進めてみました。
開発環境の要素を考える
Reactの公式ドキュメントを読み進めながら構成を考えました。
この時点で決めていたのはTypeScriptを利用することだけだったので、検討する要素は以下の2点でした。
スタイルの定義の仕方
CSS とスタイルの使用をみる限り、クラス属性を利用した制御について書かれているのみで、React はスタイルがどのように定義されているかには関心を持ちません。と記載されていました。
コンポーネント単位でCSSは定義したいと考えていたので、サードパーティのライブラリの選定が必要になりました。
利用するバンドラ・コンパイラを決める
ゼロからツールチェインを作成するに記載されています。
パッケージマネージャはnpmとしたので、バンドラ、コンパイラの選定が必要になりました。
構成を検討する
なお、公式ドキュメントに記載のあったParcelはバンドルした結果のファイル名が制御できないので、今回の想定からは外しました。
CSSのライブラリはいくつか調査しましたが、Styled ComponentsはHTML要素(例えばbuttonなど)にまで影響を与える実装の仕方だったので、今回の想定からは外しました。
これらの要素で開発環境の構成を再検討しました。
実現方法 | 理由 |
---|---|
browserify+CSS Modules | NG CSS Modulesのbrowserify公式プラグインがhighly experimentalのステータスのまま |
rollup.js+CSS Modules | NG CSS Modulesのrollup公式プラグインが存在しない |
rollup.js+Emotion | OK JavaScriptでcssを定義するので、rollup公式プラグインのnode_modulesの解決プラグインで対応できる |
実際に環境構築を行い、前提条件を満たすことができる構成はrollup.js、Emotionの組み合わせかな?と思ったので、一旦こちらの構成で進めることとしました。
環境構築手順
TypeScript、rollup.js、Emotionの構成で開発環境を構築してみたいと思います。
appディレクトリを作成
まずはappディレクトリを作成します。
ソースコードはsrcディレクトリ、ビルドした結果はpublicディレクトリに配置します。
mkdir react-app cd react-app npm init -y mkdir src mkdir -p public/scripts
ディレクトリを作成したら、npm initでpackage.jsonの最低限の設定を行います。
必要なパッケージのインストール
次に、React、TypeScript、rollup.js、Emotionに必要なパッケージのインストールを行います。
npm i -D react react-dom @types/react @types/react-dom npm i -D typescript tslib npm i -D rollup npm i -D @rollup/plugin-typescript @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-replace npm i -D @emotion/react
TypeScriptと一緒にtslibをインストールしています。これはrollup.jsのTypeScriptプラグインに必要なためです。
rollup.jsは4つプラグインをインストールしています。
プラグイン | 理由 |
---|---|
@rollup/plugin-typescript | TypeScriptを利用するため |
@rollup/plugin-commonjs | CommonJSのモジュールをES6に変換するため |
@rollup/plugin-node-resolve | ローカルのnode_modulesにあるサードパーティのライブラリをバンドルするため |
@rollup/plugin-replace | バンドルする際に対象文字列を置換するため |
@rollup/plugin-replaceは追加で入れたプラグインで、バンドル時のエラーで、process.env.NODE_ENVが解釈できないとエラーを解決するために追加しました。
プラグインのREADMEをみる限り、最も多い利用するケースのようです。
tsconfigの設定
次に、tsconfigの設定を行います。
node_modules/.bin/tsc --init
を実行するとrootディレクトリ直下にtsconfig.jsonが生成されます。
下記の内容は動くための最低限の設定です。
{ "compilerOptions": { /* Basic Options */ "target": "es5", "module": "ES2015", "lib": [ "es2015", "dom" ], "allowJs": true, "jsx": "react", "declaration": true, "declarationMap": true, "sourceMap": true, "outDir": "./public/scripts", "rootDir": "./src", "removeComments": true, /* Strict Type-Checking Options */ "strict": true, /* Module Resolution Options */ "moduleResolution": "node", "esModuleInterop": true, /* Advanced Options */ "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": [ "./src/**/*" ] }
rollup.config.jsの設定
次に、rollup.jsの設定を行います。
rootディレクトリ直下にrollup.config.jsを作成し、バンドルするための設定を定義します。
- intput:entryポイントとなるファイルを指定
- output:バンドルした結果の出力先を指定
- plugins:利用するプラグインを指定
import typescript from '@rollup/plugin-typescript'; import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import replace from '@rollup/plugin-replace'; export default { input: 'src/App.tsx', output: [ { file: 'public/scripts/bundle.js', format: "cjs", sourcemap: true }, { file: 'public/scripts/bundle.es.js', format: "esm", sourcemap: true } ], plugins: [ replace({ // alternatively, one could pass process.env.NODE_ENV or 'development` to stringify 'process.env.NODE_ENV': JSON.stringify('development') }), commonjs(), resolve(), typescript() ], };
動作確認
最後に動作確認を行うためのコードを定義します。
ディレクトリ構成と各種ファイルは以下のようにします。
index.html
index.htmlは以下の通りです。
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>test</title> </head> <body> <div id="app"> </div> <script type="text/javascript" src="./scripts/bundle.js"></script> </body> </html>
Welcome.tsx
Welcome.tsxは以下の通りです。
Emotion.jsはcss属性を利用するのですが、公式ドキュメントに2つの方法が記載されています。
- Babel Preset
- JSX Pragma
今回はBabelを利用しないようにしているので、JSX Pragmaを利用しました。
/* @jsx jsx */ import React from "react"; import { css, jsx } from '@emotion/react'; interface WelcomeProps { name: string } const style = css` color: hotpink; `; export default class Welcome extends React.Component<WelcomeProps, {}> { render() { return <div css={style}>Hello, {this.props.name}</div>; } }
App.tsx
entryファイルとなるApp.tsxは以下の通りです。
import React from 'react'; import ReactDOM from 'react-dom'; import Welcome from "./components/atoms/Welcome"; ReactDOM.render( <Welcome name="React" />, document.getElementById('app') );
これらの設定が終わったら、ビルドを実行します。
package.jsonのscriptsにbuildコマンドとして「rollup -c」を追加します。
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "rollup -c" }
追加できたら「npm run build」を実行してください。rollup.config.jsに定義した出力先にバンドルされたファイルが生成されます。
index.htmlを開くと以下の画面が表示されます。
無事、ビルドが成功しました!!!
まとめ
ひとまず、Reactで書いたコンポーネントをビルドしてブラウザで表示することができるようになりました。
設定周りの考慮など、まだ足りていない部分があるかもしれませんが、分かったらUPDATEしていきたいと思います。
当初の想定では去年の12月にはこの記事を投稿しているはずだったんですが、だいぶ手間取りました。。。
私のフロントエンド周りの知識が不足していることが分かってよかったです。
ようやく、本来のReactでのフロントエンド開発を行う下地ができたので、進めていきたいと思います。
ではでは。