TypeScript x Reactアプリの雛形を自作する(webpack, babelなど)
create-react-appやNextjsなど、中身がよくわかっていなくても最適化してくれるフレームワークが台頭している中、わざわざ自力でReactのアプリの雛形を組んでみようという取り組みです。
背景としては、
- webpackやbabelなどを使ってみたい
- create-react-appで作られたプロジェクトに配属された際、中身をわかっていないと細かい部分に対応できない
などがありました。
create react appとは
create-react-appとは、Ruby on Railsのraild new
、Vueにおけるvue create
のような新規プロジェクトの雛形を作ってくれるコマンドです。
元々の開発元はReactを作っているFacebookではないそうですが、React公式にも推奨されているコマンドになっています(Create a New React App)。
create-react-appを実行すると、以下の3つのパッケージがインストールされます。
- react
- react-dom
- react-scripts
reactとreact-domについてはreactのブログに詳しいことは書いてありますが、簡単に引用してみます。
react-native、react-art、react-canvas、react-threeなどのパッケージを見ていくと、Reactの美しさと本質はブラウザやDOMとは関係ないことが明らかになってきました。 このことをより明確にし、Reactがレンダリングできる環境をより簡単に構築するために、メインのreactパッケージをreactとreact-domの2つに分割しています。これにより、Web版のReactとReact Nativeで共有できるコンポーネントを書く道が開けます。アプリ内のすべてのコードが共有されることは期待していませんが、プラットフォーム間で同じ動作をするコンポーネントを共有できるようにしたいと考えています。
reactパッケージがプラットフォームをまたぐReactの本体、react-domパッケージがreactからdomを操作できるようにしたものといったイメージです。
また、react-scriptsについては、
- ES6や TypeScriptなどを古いブラウザでも動くレガシーなJavaScriptにコンパイルしたり、
- ソースコードを最適化したり、
- ホットリロードのようなことをしてくれていたりします。
当記事ではそんなreact-scriptsがやってくれている上記の作業を自分でやっていこうと思います。
また今回は、もはやReactでのプロダクト開発のスタンダードとなっているTypeScriptを使用します。
初期設定をする
では実際にやっていきましょう。
まずはプロジェクト用のフォルダを作り、そこに移動します。
$ mkdir react-project && cd $_
使用するパッケージマネージャーはnpm
とyarn
がありますが、ここではyarn
を使うことにします。
$ yarn init -y
(-yを指定することで初期化する際の質問を全て「Yes」で進行)
プロジェクトのフォルダ直下にpackage.jsonというファイルができます。
ここにはアプリケーションにインストールするパッケージやアプリケーションの情報を保存されます。
{
"name": "react-project",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
プロジェクト内でsrc
フォルダとpublic
フォルダを作成します。
$ mkdir src public
publicフォルダ内にindex.html
を作ります。
$ touch public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
ここで大事なのがid="root"
と書かれた部分です。
SPA(Single Page Application)ではこの一枚のhtmlにアプリケーションの実体が紐づけられます。
react, react-domを導入する
では紐付けるreactパッケージをインストールしましょう。
$ yarn add react react-dom
{
"name": "react-project",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
+ "dependencies": {
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2"
+ }
}
package.jsonに各パッケージ情報が追記されました。
次にsrc
フォルダの配下にApp.tsx
、index.tsx
を作ります。
$ touch src/App.tsx src/index.tsx
import React from 'react';
function App() {
return (
<div>
Hello world
</div>
);
}
export default App;
import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
ReactDom.render(
<App />,
document.getElementById('root')
)
コードをみてみます。
ReactDOM.render()は
- 第一引数である、Reactのコンポーネント(今回は
Appコンポーネント
を簡単に作成)をDOMに描画して、 - 第二引数に指定されたHTML要素に上書きしています。
こちらが先ほど書いたindex.html
の<div id="root"></div>
に対応しているわけですね。
(なおCRAで作成したプロジェクトには非推奨になったAPIなどを警告してくれる、Strictモードを有効にするラッパーが存在しますが、ここでは割愛します。)
TypeScriptを導入する
さて、いよいよReactのプロジェクトにTypeScriptを導入していきます。
$ yarn add -D typescript @types/react @types/react-dom
-
@types/
は型定義ファイルになります。
...
+ "devDependencies": {
+ "@types/react": "^17.0.5",
+ "@types/react-dom": "^17.0.3",
+ "typescript": "^4.2.4"
+ }
}
(なお、型定義ファイルをインストールすると、App.tsxやindex.tsxに警告が出る場合がありますが、無視してください。
次の設定で直していきます。)
TypeScriptプロジェクトのコンパイラ設定を保存しておくためのファイルである、tsconfig.json
を設定します。
$ touch tsconfig.json
{
"compilerOptions": {
"target": "es5", // コンパイル先のJSのバージョンを指定する
"lib": [ // コンパイルに含むライブラリを指定する
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, // jsファイルのimportを許可するか
"skipLibCheck": true, // 宣言ファイルの型チェックをスキップするか
"esModuleInterop": true, // CommonJSとESモジュール間との相互運用を可能にする
"allowSyntheticDefaultImports": true, // esModuleInteropと同様. こちらはコンパイル時のみに影響
"strict": true, // noImplicitAnyなどの厳密な型チェックがまとめてONになる
"forceConsistentCasingInFileNames": true, // 大文字小文字を区別してファイルを参照するか
"noFallthroughCasesInSwitch": true, // switch文のbug検出用
"module": "esnext", // コンパイル後のモジュール構文をどのモジュールシステム形式にするか設定
"moduleResolution": "node", // モジュール解決の方法をnodeにする
"resolveJsonModule": true, // jsonのimportを可能にするか
"isolatedModules": true, // 全てのファイルを単一のモジュールとしてコンパイルする
"noEmit": true, // ファイルを出力しないようにする.TSのコンパイルはbabelで行う
"jsx": "react-jsx" // JSX 構文をそのままにしておくか React の構文に書き換えるかを指定するためのオプション
},
"include": [ // コンパイル対象にするファイルを指定する
"src"
],
"exclude": ["node_modules", "build"] // コンパイル対象から外すファイルを指定
}
コメントで軽く説明を加えましたが、ドキュメントに詳細が載っています。
Babel
では次にBabelについてみていきます。
BabelとはJavaScriptのコンパイラです。
主に、es2015以降の新しい構文をes5の構文に変換したり、JSX構文やTypeScriptの変換に使用されています。
まずは各パッケージをインストールします。
$ yarn add -D @babel/core @babel/cli @babel/preset-env @babel/preset-typescript @babel/preset-env @babel/preset-react
先頭二つはdefaultでほぼ必要になります。
-
@babel/core
- Babel本体
-
@babel/cli
- Babelをターミナルから利用できるようにするもの
presetとはコンパイルに必要なプラグインのまとまりです。
-
@babel/preset-env
- ECMAScript用のプラグインのまとまり
-
@babel/preset-typescript
- TypeScript用のプラグインのまとまり
-
@babel/preset-react
- React用のプラグインのまとまり
また、CRAではnode_modules/@babel
で実体を確認できます。
Babelのインストールができたので、設定をしていきます。
$ touch .babelrc
{
"presets": [
"@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"
]
}
こちらも色々と設定できそうですが、簡単にやってみました。
webpack
次にwebpackについてみていきます。
webpackとは、JavaScriptモジュールを一つまたは複数の指定したファイルにバンドル(束ねる)してくれるツールです。
JavaScriptと言いましたが、ローダーを入れることでCSSや画像などもバンドルしてくれます。
$ yarn add -D webpack webpack-cli webpack-dev-server babel-loader html-webpack-plugin
$ touch webpack.config.js
const path = require('path'); // outputパスに絶対パスを指定するため
const HtmlWebpackPlugin = require('html-webpack-plugin'); // plugin
module.exports = {
entry: path.resolve(__dirname, "src", "index.tsx"), // ビルドを始める際の開始点となるファイルを指定. srcフォルダのindex.tsxを起点としている
output: { // bundleファイルの出力先を指定. distフォルダのbundle.jsに吐き出すようにしている
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
mode: "development", // development or production or nodeの指定が可能. それぞれに最適化されてwebpackが実行される
module: { // 各種ローダーの設定を行う
rules: [
{
test: /\.[jt]sx?$/, // jsx or tsxで終わるファイルがあればbundleファイルに追加する前にbabel-loaderで変換する
use: ["babel-loader"],
exclude: /node_modules/,
},
],
},
plugins: [ // ビルドする前の変換処理にタスクを追加する
new HtmlWebpackPlugin({ // webpackバンドルに対応するhtmlを自動生成するプラグイン
template: path.resolve(__dirname, "./public/index.html"),
}),
],
resolve: { // .jsxを解決できるようにする
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
};
実際に動かしてみましょう。
package.jsonに次のように追記します。
+ "scripts": {
+ "start": "webpack serve --config webpack.config.js --env env=dev",
+ "build": "webpack --config webpack.config.js --env env=prod"
+ },
webpack serve
でlocalにserverが立ち上がります。
--config webpack.config.js
でserveするwebpackファイルを指定しています。
--env env=dev
でwebpackを実行する環境を指定します。
これらをスクリプトとしてstart
として登録することで実行することができます。
$ yarn start
ローカルでReactのアプリケーションが立ち上がりました!
ここからはおまけです。
prettier, eslint
まずはコードフォーマッターであるprettierをインストールしていきます。
主にコードの形をチームで強制するのに使用されます。
$ yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
$ touch prettierrc.js
module.exports = {
semi: false, // セミコロンをつけるか
jsxSingleQuote: true, // jsxでsingle quotationを使うか
singleQuote: true, // single quotationを使うか
printWidth: 120, // 折り返す行の長さを指定
arrowParens: "always", // アロー関数が()を常につけるか
trailingComma: "all" // 末尾にカンマをつけるか
};
設定オプションはたくさんあるので好みになるかと思います。
私はこの構成でいつもコードを書いています。
eslintの設定もしていきましょう。
eslintはコードフォーマッターと構文チェックができるツールです。
prettierと併用することでそれぞれの良いところどりをして利用されることが多いです。
$ yarn add -D eslint @typescript-eslint/eslint-plugin eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-import @typescript-eslint/parser eslint-plugin-jsx-a11y
$ touch .eslint.js
module.exports = {
env: {
browser: true,
node: true
},
extends: [ // 上から順番に適用され、どんどん上書きされるため優先度の高いものは一番下に指定する
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:jsx-a11y/recommended",
"plugin:prettier/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
},
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/no-var-requires": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
"react/jsx-uses-react": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
},
};
vscodeを使っている方はeslintやprettierの拡張機能を入れるとより開発体験が向上すると思います。
ここまでお付き合いいただきありがとうございました!
拙い点や間違いなどございましたが、コメントをいただけると嬉しいです🙏
参考資料
りあクト!シリーズ
yarn
tsconfig.json
babel
webpack
prettier
eslint
Discussion