この記事について
主にバックエンドしか触る機会がなく、フロントエンドに弱い自分の備忘録です。
reactを触った事なく、どんなものか手元で触ってみたいな〜と思ってはいるものの、
jsx
webpack
babel
とか何よ??
って状態だったので、触ってみましたというお話。
公式にあるようにreact+typescriptの環境って
npx create-react-app {適宜} --template typescript
と叩けば一式揃いますが、
何がどう入って何をしてくれているのか、無知な私にはよく理解できなかったので、
一個一個入れて確認していきながら、
ググって得た超基礎知識のおさらいをしていきますという記事になります。
目次
- 環境構築
- webpack入れる
- react入れる
- jsxの記法を使ってみる
- typescriptで書いてみる
- 最後に
環境構築
では環境作って行きます。
webpack入れる
まずは、package.json作ります
npm init
次に、jsやcss、画像なんかを一個にまとめてるくれるモジュールバンドラであるwebpackを入れます。
cliとローカルサーバ立ち上げてくれるwebpack-dev-server
も入れます。
ローカルにしかいらないので --save-dev
(-D
)を指定しておきます。
npm install --save-dev webpack webpack-cli webpack-dev-server
ここまでで何が入ったかというと、
.
├── node_modules
├── package-lock.json
└── package.json
まあこれだけです。
ここまでで何ができるようになったか、一回みてみます。
public
と src
のディレクトリを作って、index.htmlとindex.jsを用意します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<h1 id="h1-tile">テストです</h1>
</body>
</html>
import {content} from "./content";
content();
export function content() {
const content = document.createElement('div');
content.innerText = 'index.js実行されたよ!!!!';
const title = document.getElementById('h1-title');
title.appendChild(content);
}
ちょっと意味わからん分け方してますが、最終的にこれをreactに起こすという事をしたいが為にファイル分けてます。
package.json
のscriptに追記して、startコマンドでdev-serverが立ち上がるようにしておきます。
"scripts": {
"start": "webpack-dev-server"
}
さらに、まとめたファイルの出力先と名前を指定してみます。
webpack.config.jsを作成してdevServerについて記載します
const path = require('path');
module.exports = {
+ entry: './src/index.js',
+ output: {
+ path: path.join(__dirname, 'public'),
+ filename: 'output.js'
+ },
devServer: {
static: {
directory: path.join(__dirname, 'public'), // publicをドキュメントルートに指定
},
port: 8888, // ポートは8888に
},
};
ここまででこんな感じ
.
├── node_modules
├── package-lock.json
├── package.json
├── public
│ └── index.html
├── src
│ ├── index.js
│ └── content.js
└── webpack.config.js
起動してみます。
npm start
8888ポートにアクセスしてみます。
起動はしますが、index.jsが呼ばれてないようです。
output.jsを読み込むようにindex.htmlを修正します
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<h1 id="h1-tile">テストです</h1>
+ <script src="output.js"></script>
</body>
</html>
反映されました。
ここでindex.htmlをブラウザで直接開いてみます。
こっちは、js読み込まれてません。
output.jsは今の所物理的に存在してないのでそらそう。
なので、webpackにoutput.jsを作ってもらいます。
npx webpack --mode development
> asset output.js 4.33 KiB [emitted] (name: main)
> runtime modules 670 bytes 3 modules
> cacheable modules 291 bytes
> ./src/index.js 61 bytes [built] [code generated]
> ./src/content.js 230 bytes [built] [code generated]
> webpack 5.73.0 compiled successfully in 45 ms
うんpublic下にoutput.jsが生成されました。
これでブラウザで直接index.htmlを開いてもjsがはしります。
ちょっとcontentのテキスト部分を書き換えてみます。
npm startした方はリコンパイルが走って画面からも、更新が確認できました。
ファイルを直接参照している方では反映確認できません。
という事で、dev-serverは、ローカルサーバ立ち上げ + assetsをウォッチして常に最新のoutput.jsを参照するように変更してくれる模様でした。
次に進みたいと思います。
react入れる
物理生成されたpublic/output.jsは削除してreact入れます
npm install react react-dom
各種jsをreactで書いてみます
import React from 'react';
export const Content = () => {
return React.createElement('div', null, "index.js(react)が実行されたよ");
};
import React from "react";
import * as ReactDOM from "react-dom/client";
import {Content} from "./content";
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(React.createElement(Content, null, null));
content.jsに記載した関数コンポーネントを、
メインとなるindex.jsの方で呼び出している形です。
content.jsの方の関数コンポーネントが何してるかというと、
createElementをでdivを追加しているだけですね。
npm startします
オッケイそうです。
jsxの記法を使ってみる
createElementで毎回記載するのは大変で、
そういうときのために拡張言語のjsxが登場します。
createElementで書いてるような部分を、直接htmlタグを記載する感覚で書くことができます。
ちょっとjsxの書き方を使って変更してみましょう。
↓↓
import React from 'react';
export const Content = () => {
return (
<div>
index.jsxが実行されたよ!!!!
</div>
);
};
import React from "react";
import * as ReactDOM from "react-dom/client";
import {Content} from "./content";
const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(
<Content />
);
npm start すると
You may need an appropriate loader to handle this file type
と怒られます。
jsxの記法をjsに変換しなければならないので、
ここで、babelが登場します
npm install --save babel-loader
npm install --save-dev @babel/preset-react
webpackの設定で、js/jsxの書き換えに使うモジュールを指定します
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader'], // ← jsとjsxファイルはbabel-loader使ってねー
},
],
},
resolve: {
extensions: ['*', '.js', '.js'],
},
もっかいnpm startします
to the 'presets' section of your Babel config to enable transformation.
babelのconfigが必要みたいなので追加します
{
"presets": ["@babel/preset-react"]
}
今度こそ、お願い。という気持ちでnpm startします
おっけい、反映されました。
srcの2ファイルはjsxが使われているので一応.js ⇨ .jsx、と拡張子変えてます。
ここまででこんな感じ
.
├── node_modules
├── package-lock.json
├── package.json
├── public
│ └── index.html
├── src
│ ├── index.jsx
│ └── content.jsx
├── webpack.config.js
└── babel.config.json
さあ次に進みます
typescriptで書いてみる
reactはtypescriptで書いて初めて本領発揮するので、typescriptを入れます
npm install --save-dev typescript ts-loader
npm install --save-dev tslint tslint-loader tslint-config-airbnb
npx tsc --init
npm i --save-dev @types/react
npm i --save-dev @types/react-dom
webpack.config.jsを改変します
entry: './src/index.tsx',
module: {
rules: [
{
loader: 'ts-loader', // ts-loaderでトランスパイルする
test: /\.tsx?$/,
exclude: /node_modules/,
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js' ] // .tsx、.tsを追記した、jsxは不要
},
jsxファイルをtypescriptで記載してみましょう
import React from "react";
import * as ReactDOM from "react-dom/client";
import {Content} from "./content";
const root = ReactDOM.createRoot(document.getElementById('app') as HTMLDivElement);
root.render(
<Content
text={"index.tsxが実行されたよ!"}
/>
);
import React from "react";
interface ContentProps {
text: string;
}
export const Content = (props: ContentProps) => {
return (
<div>{props.text}</div>
);
};
ここでtypescriptの型推論の恩恵を感じてみたかったので、
今ままでdivの文字列は、Contentコンポーネントの方で保持していましたが、
あえて、index.tsx側からContentコンポーネントにpropsを渡す形をとってみました。
// ↓↓↓ ここ !!
export const Content = (props: ContentProps) => {
return (
<div>{props.text}</div>
);
};
この部分で、Contentコンポーネントの引数は ContentProps
なるものである事を示しています。
ContentProps
って誰だよ、となるわけですが、ひとつ上の所で
interface ContentProps {
text: string;
}
と、 text
というstringのプロパティを持つんだよ。と型定義してます。
これ何が嬉しいかって、
index.tsxでContentを呼ぶときに、何が必要かエディタが教えてくれます。
numberとかいれるとガチギレされます。
もちろんトランスパイルで怒られますし、開発するときもいろいろ嬉しいですね。
さあ、画面開いてみます。
反映されたのでこれにて完。
最後に
jsxのトランスパイルはbabelがやってくれましたが、
tsxのトランスパイルの際にts-nodeはjsxの部分もまとめてjsにしてくれています、
この時点でbabelは不要という事になりますね。
プロジェクト内で、
.jsx と .tsx を混在させるようなときは双方使っていく形になるのでしょう。
なにはともあれ、無事
シェフの気まぐれソースコード react by typescript ~jsxを添えて~ の完成です。
今まで意味わからんかった単語達と、少し仲良くなれた気がします。
参考文献