88
76

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 5 years have passed since last update.

npmのインストール後、ReactをTypeScriptで書くまで

Last updated at Posted at 2018-05-13

はじめに

なんだかんだWebのフロントで一番きついのは、環境構築だと思います。
フロントを始めた時に、こんなドキュメントがあればハマらなかっただろうな…と思うことを備忘します。
ただ、こんな記事を書いておいてなんですが、よくわからなければ公式ドキュメントとエラーメッセージをよく読むのが解決への近道です。

前提条件

タイトルにも書いてある通りNode.jsとnpmのインストールは一通り終わってる前提です。
そして、型がないと不安でプログラムが書けない人向けです。

目指す姿

  • Linterできちんとビルド前に静的チェックされる
  • TypeScriptで書いたコードが無事index.htmlとかとともにディレクトリに出力される
  • ちゃんとデバッグできるようにする

まず.gitignore

何はともあれ、.gitignoreを最初にコミットします。
github/gitignoreからNode.gitignoreとIDEのxxx.gitignore(筆者はWebstorm使ってるのでJetBrains.gitignore)、あと必要に応じてmacOS.gitignoreなどをマージして作ってます。

package.jsonを作る

参考

package.jsonとはnpmの設定ファイルです。
ですので、これを置いておけば、同じ環境のnpmならnpmコマンドは同じように動きます。
作り方は簡単でリポジトリルートで

npm init

するだけです。そうすると

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.

が表示されて、幾つかの質問をされるので、それに適当に応えるだけです。
殆どの項目はnpmモジュールとして切り出す際に使われるであろう項目だと思うので、そこまで真剣になる必要はないと思っています。
一応、聞かれる項目は以下の通り。

package name
デフォルトでディレクトリの名前が入ってるので、そのままで
version
デフォルトで1.0.0が入ってるので、特段こだわりなければそのままで
description
このモジュールの説明を書いておきましょう
entry point
このモジュールがrequireされたときのエントリーポイントです、npmモジュールとして切り出したりするなら別ですがウェブサイトをそのままnpmパッケージにすることはないと思うので、デフォルトのままでOKです
test command
ここではテスト実行時に走らせるコマンドを登録するのですが、テストについては別記事で解説する予定なので、一旦空のままにしておきます
git repository
デフォルトで正しいアドレスが入ってれば、そのままでOK
keywords
キーワードなるものの設定、スペース区切りで複数設定可能
author
作者情報、これもgitのユーザー名がデフォルトで入ると思うので、こだわりなければそのままで
license
ライセンス情報、利用可能な文字列は[SPDX](https://spdx.org/licenses/)を参照

これらを指定するのが面倒なら

package.jsonでprivateというパラメータを使用することができます。これをtrueにすることで、上記の情報を設定しなくてもエラーが出なくなります。その場合は、以下のようなpackage.jsonを自分で配置すればOKです。

package.json
{
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

必要なモジュールをインストールする

それでは必要なモジュールをインストールします。

React

↓のコマンドでインストールできます。

npm install --save react react-dom
npm install --save-dev @types/react @types/react-dom

@types/xxxというやつは型定義ファイルというもので、JavaScriptで書かれたモジュールのTypeScriptの型を定義したものです。基本的にはTypeScriptでnpmライブラリを使うならば、@types/xxxを一緒にインストールしておけばよろしいのですが、パッケージそのものに型定義がついているものもあるので注意が必要です。(大抵は公式にインストールの仕方書いてあるし、間違えて@types/xxxインストールしたらnpmがwarnしてくれます。)
詳しくはこちらを参照。

React以外

こっちが鬼門で、筆者は結局

  1. TypeScriptで書く(じぶん)
  2. TSLint通す(tslint)
  3. JavaScriptに変換する(typescript)
  4. いっこのjsファイルにする(webpack)

という流れに落ち着いてます。create-react-appとかparcelとかgulpとか色々ありまくりなのですが、これが、それなりの自由度で、それなりに息が長そうな組み合わせだと勝手に思ってます。
それぞれについてちょっとした説明と、インストールの仕方を以下に書いておきます。

webpackのインストールの仕方

参考

webpackはたくさんのjsファイルを1個にしてくれるすごいやつです。あと、いろんなloaderを注入できるのでこいつでビルドチェインを作ります。
webpackのインストールは↓

npm install --save-dev webpack webpack-cli

TypeScriptのインストールの仕方

参考

こいつのおかげで、型が付きます!ありがたいです!ts-loaderをwebpackに注入して使うとビルドの中で変換をやってくれます。
インストールは↓

npm install --save-dev typescript ts-loader

Tslintのインストールの仕方

参考

TypescriptのLinterとwebpackにいれるtslint-loaderをインストールしておきます。
今回は設定はairbnbのものを使うので、それもまとめてインストールします。
インストールは↓

npm install --save-dev tslint tslint-loader tslint-config-airbnb

html-webpack-pluginをインストールしておく

参考

index.htmlをビルドの時にターゲットディレクトリに配置してくれるすごいやつです。これがあれば細かいことは気にせず、ターゲットディレクトリをまるっとあげるだけでOKになります。
インストールは↓

npm install --save-dev html-webpack-plugin

webpack-serveもインストールしておく

参考

開発用の簡易Webサーバー。これがあるとブラウザで見れるので、いい感じ。
インストールは↓

npm install --save-dev webpack-serve

Babelとかいうやついらんの?

当初これがよくわかりませんでした。babel-preset-envがどうのこうのとか色々書いてあって、何がどうなっとるんだと。
結論から言うといりません。BabelはナウでヤングなJavaScriptをどこでも動くようにするものです。TypeScriptがもし仮にナウでヤングなJavaScriptしか吐けないのであれば、組み合わせて使う必要があると思いますが、TypeScriptはビルドターゲットをES2015にできますので、これだけで充分です。

webpackの設定

webpackは設定そのものがJavaScriptになっていて自由度が非常に高いですが、最低限のことを書くだけならとても簡単だし、モジュールが注入しやすいです。
webpackは実行時に設定ファイルを指定できますので、いくつか置いておいて叩きワケできるようにしておくとよいです。

本番用

本番用のビルド設定は、デバッグのための設定なしでみたいな感じで設定します。
ソースとコードコメントというかたちで何をやっているか書いておきます。

webpack.config.prod.js
const path = require('path');                             // 絶対パスに変換するために
const htmlWebpackPlugin = require('html-webpack-plugin'); // index.htmlをビルドチェインの中で作っちゃう

module.exports = {
    mode: 'production',        // 使える文字列が決まってる、本番用なのでproduction。
    entry: './src/index.tsx',  // エントリポイントの指定、src下に書いていくので src/index.tsxにしとく
    module: {
        rules: [
            {                             // Linterを走らせる
                enforce: 'pre',           // ビルド前処理だよってこと
                loader: 'tslint-loader',  // tslint-loaderを使う
                test: /\.tsx?$/,          // tslint-loaderに渡すファイルの正規表現。xxx.tsとxxx.tsxの正規表現。
                exclude: [                // 渡さないファイル
                    /node_modules/
                ],
                options: {
                    emitErrors: true      // これ設定しとくとTSLintが出してくれたwarningをエラーとして扱ってくれる、要するに-Wall
                }
            },
            {
                loader: 'ts-loader',      // ts-loaderを使う、こいつがトランスパイルしてくれる
                test: /\.tsx?$/,
                exclude: [
                    /node_modules/
                ],
                options: {
                    configFile: 'tsconfig.prod.json' // TypeScriptのコンパイル設定ファイル
                }
            }
        ]
    },
    resolve: {
        extensions: [ '.tsx', '.ts', '.js' ]    // importの時に、これらの拡張子は解決してもらえる、要するにHoge.tsxをimport Hoge from './Hoge'みたいに書ける
    },
    output: {
        filename: 'static/js/bundle.js',        // 仕上がりファイルの置き場
        path: path.resolve(__dirname, 'dist')   // 出力ディレクトリの指定の絶対パス
    },
    plugins: [
        new htmlWebpackPlugin({
            template: "index.html"    // 同じ階層にあるindex.htmlを元に、デプロイ用のindex.htmlを作って出力ディレクトリに配置してくれる
        })
    ]
};

開発用

開発用のビルド設定は、デバッグのための設定をいれてみたいな感じで設定します。
本番用とのdiffというかたちで乗っけておきます。

webpack.config.dev.js
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
-    mode: 'production',
+    mode: 'development',
    entry: './src/index.tsx',
+    devtool: 'inline-source-map',    // デバッグできるように
    module: {
        rules: [
            {
                enforce: 'pre',
                loader: 'tslint-loader',
                test: /\.tsx?$/,
                exclude: [
                    /node_modules/
                ],
                options: {
                    emitErrors: true
                }
            },
            {
                loader: 'ts-loader',
                test: /\.tsx?$/,
                exclude: [
                    /node_modules/
                ],
                options: {
-                    configFile: 'tsconfig.prod.json'
+                    configFile: 'tsconfig.dev.json'
                }
            }
        ]
    },
    resolve: {
        extensions: [ '.tsx', '.ts', '.js' ]
    },
    output: {
        filename: 'static/js/bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new htmlWebpackPlugin({
            template: "index.html"
        })
    ]
};

ブラウザ確認用

ブラウザ確認用のビルド設定は、開発用のビルド設定にwebpack-dev-serverの設定を加えます。
こちらは開発用とのdiffというかたちで乗っけておきます。

webpack.config.preview.js
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: './src/index.tsx',
    devtool: 'inline-source-map',
    module: {
        rules: [
            {
                enforce: 'pre',
                loader: 'tslint-loader',
                test: /\.tsx?$/,
                exclude: [
                    /node_modules/
                ],
                options: {
                    emitErrors: true
                }
            },
            {
                loader: 'ts-loader',
                test: /\.tsx?$/,
                exclude: [
                    /node_modules/
                ],
                options: {
                    configFile: 'tsconfig.dev.json'
                }
            }
        ]
    },
    resolve: {
        extensions: [ '.tsx', '.ts', '.js' ]
    },
    output: {
        filename: 'static/js/bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new htmlWebpackPlugin({
            template: "index.html"
        })
    ],
+    serve: {
+        content: path.resolve(__dirname, 'dist'),
+        port: 3000,
+    }
};

TypeScriptとTSLintの設定

webpack内で設定した、tsconfigとLinterの設定をここでつくります。

tsconfig.xxx.json

参考

デバッグ用の設定を入れる入れないを切り分けるので、2つ作ってます。
違いは"sourceMap": trueの部分だけですので、開発用は本番用を拡張しています。

tsconfig.prod.json
{
    "compilerOptions": {
        "module": "es6",
        "target": "es5",
        "jsx": "react",
        "lib": ["es2018", "dom"],
        "moduleResolution": "node",
        "removeComments": true,
        "strict": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noImplicitReturns": true,
        "noFallthroughCasesInSwitch": true
    }
}
tsconfig.dev.json
{
    "extends": "./tsconfig.prod.json",
    "compilerOptions": {
      "sourceMap": true
    }
}
sourceMap
デバック用の設定
module
変換のためのモジュール指定、変換先がES2015のときはES6かES2015を指定しとく
target
できあがりはだいたいの環境で動くようにES2015にしとく
jsx
React用にreactを指定
lib
標準ライブラリの読み込み、es201xとdomを指定しておく
moduleResolution
モジュールの解決方式を指定、Node.js方式にするためnodeを指定
removeComments
ビルド成果物からコメントを取り除く
strict
怪しげな書き方をできないように
noUnusedLocals
未使用のローカル変数禁止
noUnusedParameters
未使用のパラメータ禁止
noImplicitReturns
怪しげなReturn禁止
noFallthroughCasesInSwitch
switchのときのdefaultし忘れ防止

tslint.json

参考

ベースはairbnbのものを使ってインデントサイズだけ4にしてます。
tslint.jsonというファイル名で他と同様ルートにおいておけばいい。

tslint.json
{
    "extends": "tslint-config-airbnb",
    "rules": {
        "ter-indent": [true, 4]
    }
}

package.jsonに設定を追加

最後に、npm run xxxでビルドとかできるように設定を追加しておきます。
package.jsonのscriptsというエレメント内にすでにtestがあるはずなので、その下に追加しましょう。
testの行は、今回使いませんので削っておきましょう。

  "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "start": "webpack-serve --config webpack.config.preview.js",
+    "build": "webpack --config webpack.config.dev.js",
+    "build:prod": "webpack --config webpack.config.prod.js"
  },

これで、npm run startでブラウザ確認用、npm run buildで開発用、npm run build:prodで本番用ビルドが走ります。

最後に

これで環境は完成です。こんなディレクトリ構成になっているはずです。

./
 ├ node_modules        // 外部パッケージのインストール先、バージョン管理対象外
 ├ .gitignore
 ├ package.json
 ├ package-lock.json   // npmが勝手に作る管理用ファイル、バージョン管理対象
 ├ README.md
 ├ tsconfig.dev.json
 ├ tsconfig.prod.json
 ├ tslint.json
 ├ webpack.config.dev.json
 └ webpack.config.prod.json

ここに実際にindex.htmlとTypeScriptソースを追加してみます。

index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>react sample</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
</html>
src/index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';

ReactDOM.render(
    <div>Hello world</div>,
    document.getElementById('root'));

npm run buildもしくは、npm run build:prodするとdistディレクトリに全てが出力されます。

./
 ├ dist
 │  ├ index.html
 │  └ static
 │     └ js
 │        └ bundle.js
 │
 ├ node_modules
 ├ src
 │  └ index.tsx
 ├ .gitignore
 ├ index.html
 ├ package.json
 ├ package-lock.json
 ├ README.md
 ├ tsconfig.dev.json
 ├ tsconfig.prod.json
 ├ tslint.json
 ├ webpack.config.dev.json
 └ webpack.config.prod.json

またnpm run startすれば、http://localhost:3000 でHello worldが表示されます。(事前ビルド不要)

できあがったソース群はこちら
https://github.com/IgnorantCoder/webpack-typescript-react-sample

おまけ (Debug on WebStorm)

WebStormによるデバッグがやや癖があったのでおまけに記載。

参考

  • Run > Edit Configurations
  • +ボタンからJavaScript Debugを追加、URLにhttp://localhost:3000 を追加
  • npm run startしてから、この追加した構成をDebug buildするとアタッチされる
88
76
1

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
88
76

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?