1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

react + typescriptの開発環境を、モジュールちょっとずつ入れながら作ってみる

Last updated at Posted at 2022-06-09

この記事について

主にバックエンドしか触る機会がなく、フロントエンドに弱い自分の備忘録です。

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

まあこれだけです。

ここまでで何ができるようになったか、一回みてみます。

publicsrcのディレクトリを作って、index.htmlとindex.jsを用意します。

public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
</head>
<body>
<h1 id="h1-tile">テストです</h1>
</body>
</html>
src/index.js
import {content} from "./content";

content();
src/content.js
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が立ち上がるようにしておきます。

package.json
"scripts": {
  "start": "webpack-dev-server"
}

さらに、まとめたファイルの出力先と名前を指定してみます。

webpack.config.jsを作成してdevServerについて記載します

webpack.config.js
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ポートにアクセスしてみます。

Screenshot 2022-06-10 2.17.59.png

起動はしますが、index.jsが呼ばれてないようです。

output.jsを読み込むようにindex.htmlを修正します

public/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>

Screenshot 2022-06-10 2.21.20.png

反映されました。

ここでindex.htmlをブラウザで直接開いてみます。

Screenshot 2022-06-10 2.19.56.png

こっちは、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で書いてみます

src/content.js
import React from 'react';

export const Content = () => {
    return React.createElement('div', null, "index.js(react)が実行されたよ");
};
src/index.js
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します

Screenshot 2022-06-10 2.24.39.png

オッケイそうです。

jsxの記法を使ってみる

createElementで毎回記載するのは大変で、
そういうときのために拡張言語のjsxが登場します。

createElementで書いてるような部分を、直接htmlタグを記載する感覚で書くことができます。

ちょっとjsxの書き方を使って変更してみましょう。

↓↓

src/content.jsx
import React from 'react';

export const Content = () => {
    return (
        <div>
            index.jsxが実行されたよ!!!!
        </div>
    );
};
src/index.jsx
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の書き換えに使うモジュールを指定します

webpack.config.js
    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が必要みたいなので追加します

babel.config.json
{
  "presets": ["@babel/preset-react"]
}

今度こそ、お願い。という気持ちでnpm startします

Screenshot 2022-06-10 2.32.02.png

おっけい、反映されました。

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を改変します

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で記載してみましょう

src/index.tsx
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が実行されたよ!"}
    />
);
src/content.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を呼ぶときに、何が必要かエディタが教えてくれます。

Screenshot 2022-06-10 2.43.12.png
Screenshot 2022-06-10 2.43.32.png

numberとかいれるとガチギレされます。

Screenshot 2022-06-10 2.43.50.png

もちろんトランスパイルで怒られますし、開発するときもいろいろ嬉しいですね。

さあ、画面開いてみます。

Screenshot 2022-06-10 2.45.26.png

反映されたのでこれにて完。

最後に

jsxのトランスパイルはbabelがやってくれましたが、
tsxのトランスパイルの際にts-nodeはjsxの部分もまとめてjsにしてくれています、
この時点でbabelは不要という事になりますね。

プロジェクト内で、
.jsx と .tsx を混在させるようなときは双方使っていく形になるのでしょう。

なにはともあれ、無事
シェフの気まぐれソースコード react by typescript ~jsxを添えて~ の完成です。

今まで意味わからんかった単語達と、少し仲良くなれた気がします。

参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?