フロントエンド

create-react-appは使用しない!webpackでReactの環境構築

2022年7月2日

目次 [閉じる]

    目的

    この記事は、webpackを使用し、React の開発環境を作成することを目的とした記事になります。

    方法

    方法として、webpackをベースにReact+ Typescript + Sass の環境構築をcreate-react-appを使用せず最小単位で作成します。
    その環境構築を通して、裏ではどのようにフロントエンドが動作していたのかを把握することを目指しました。
    以下、今回の記事の登場技術です。

    • webpack
    • bubel
    • Sass
    • Typescript
    • React

    webpack

    webpackとは

    モジュールバンドラー
    複数のフロントエンドで必要なファイルを1つのjavascriptファイルに束ね(バンドル)出力してくれるツール。
    他にもモジュールバンドラーはありますが、現状webpackが1強のようです。

    メリット

    • 複数のファイルに分け開発できる。
    • 依存関係を解決してくれる。
    • 1ファイルにまとめることでウェブコンテンツの読み込み速度が向上する。
    • jsだけでなく、css、画像ファイルなどもバンドルできる。

    Babel

    Babelとは?

    javascriptに特化したコンパイラー

    メリット

    • Javascriptの新しい記法を古い記法に変換してくれる。
    • 古い記法に変換することで、IEなどのブラウザにも対応させることができる。

    Sass

    Sassとは?

    cssを拡張子、記述しやすく、みやすくしたスタイルシート。

    メリット

    • ネストしてcssを記述できる。
    • 変数を利用できる。
    • メソッドを記述できる。

    Typescript

    Typescriptとは?

    型を定義できるAltJs

    メリット

    • 変数に型を定義できるため、コンパイルエラーでエラーを発見しやすい。

    環境構築スタート

    今回の最終着地点は、webpack + Typescript + Sass + Reactの開発環境です。
    以下のような流れで構築を行います。

    1. package.jsonの作成
    2. webpackのインストール
    3. webpackの設定
    4. ローダーの設定
    5. Htmlファイルの作成
    6. Scssファイルの作成
    7. Reactファイルの作成
    8. 動作確認

    ※node npmはインストール済みで話を進めます。

    最終的なディレクトリ構成は以下のようになっています。

    project_name/
         |
         |- dist/
         |   | - index.html
         |   | - main.js
         |   L - style.css
         |
         |- node_module/
         |
         |- public
         |    L style
         |        L style.scss
         |
         |- src
         |   | - App.tsx
         |   L - index.tsx
         |
         |- package-lock.json
         |- package.json
         |- tsconfig.json
         L- webpack.config.js
         

    1. npm init で package.jsonを作成

    まず、package.jsonファイル作成のため以下のコマンドを打ちます。
    以下のコマンドで

    npm init -y

    package.jsonはプロジェクトが依存するパッケージをまとめたものです。
    以下のような内容で記述されています。
    package.json

    {
      "name": "frontend_dev",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "author": "",
      "license": "ISC",
     //開発環境でのみ使用するパッケージが入る。
      "devDependencies": {
      },
      //実行環境で使用するパッケージが入る。
      "dependencies": {
      }
    }
    
    

    今後、devDependencies、dependenciesの部分にパッケージを追加していきます。

    2. webpackのインストール

    以下のコマンドでwebpackで必要なパッケージのインストール。

    npm i -D webpack webpack-cli

    3. webpackの設定

    パッケージのインストール後、webpackの設定を記述していくファイルを以下のコマンドで作成します。

    touch webpack.config.js

    以下、webpack.config.jsファイルの初期設定

    webpack.config.js

    const path = require("path");
    module.exports = {
      //ビルドのタイプを設定(デフォルトではproduction)
      mode: "development",
      //起点となるファイルの場所
      entry: "./src/index.tsx",
      //バンドルファイルを出力
      output: {
        path: path.join(__dirname, "dist"),
        filename: "bundle.js",
      },
      //import時にファイルの拡張子を省略する設定
      resolve: {
        extensions: [".js",".jsx",".ts", ".tsx", ".json"],
      },
      //リアルタイムで変更を確認しながら開発を行う設定
      devServer: {
        static: {
          directory: path.join(__dirname, "dist"),
        },
        //3000番ポートに吐き出します。
        port: 3000,
      },
      //サーバー対応なのか、ブラウザ対応なのかの指定(今回はブラウザなのでweb)
      target: ["web"],
     //ローダーの設定
     module:{}
    };
    
    

    mode
    ビルドのタイプを指定するプロパティです。
    developとproductionの2つを指定できます。
    develop(開発環境)

    • ビルド時間が短くなる
    • 容量が大きくなる

    production(本番環境)

    • ファイルが圧縮され容量が小さくなる。
    • 難読化される。
    • ビルド時間が長い。

    entry
    起点となるファイルを指定する。
    設定したファイルを起点とし、そのファイルから依存関係にあるファイルを芋づる式に特定し、全てのファイルを1つのファイルにバンドルするために、起点ファイルをここで設定する。
    今回はsrc/index.tsxファイルを起点ファイルにしています。
    output
    バンドルファイルを出力する場所を指定する。
    バンドルされた1つのファイルを出力する場所を指定。
    今回は、distディレクトリ内に、bundle.jsというファイルで出力しています。
    resolve
    ファイルの拡張子を省略できる。
    devServer
    ローカルでサーバーを立ち上げる。
    リアルタイムで変更を確認しながら開発が可能。
    target
    サーバーなのか、ブラウザなのかの指定。
    module
    ローダーの設定を記述する。

    4. ローダーの設定

    ローダーとは、あるファイルを別のファイルに変換するものです。
    例えば、scssファイルをcssファイルに変換する。Typescriptファイルをjavascriptファイルに変換するなどです。
    今回設定するのは、

    • Scss
    • Typescript
    • Babel

    です。

    Scssを扱う

    今回は、Scssを使用するので、必要なローダーたちの設定を記述します。
    以下のコマンドを打ち、必要なプラグインをインストールします。

    npm i -D style-loader css-loader sass-loader sass
    • style-loader
    • バンドルされたcssファイルをhtmlで使用するためのloader
    • css-loader
    • cssファイルをjavascriptファイルにバンドルするためのloader
    • scss-loader
    • scssファイルをcssファイルにコンパイルするloader

    次に、loaderをwebpackのmoduleプロパティ内で設定します。

    webpack.config.js

    const path = require("path");
    module.exports = {
      //省略
      //loaderの設定を記述する場所
      module: {
        rules: [
          {
          {
            test: /\.(css|scss)$/,
            use: ["style-loader", "css-loader", "sass-loader"],
          },
        ],
      },
    };

    module
    loaderを設定するプロパティ

    rules
    各loaderのルールを設定するプロパティ

    test
    対象ファイルを設定するプロパティ
    ファイルの拡張子を指定、今回はscssが対象なのでscssを指定

    use
    どのloaderをどの順番で使用するかの設定。
    後ろから順番に処理がされます。
    つまり今回は、
    sass-loader → css-loader → style-loader
    の順番ですので、
    scssをcssに変換 → 変換されたcssをjavascriptファイルにバンドル → バンドルされたcssファイルをHTMLで使用。
    以上の設定でscssを使用することができます。

    Typescriptを扱う

    Scssの設定ができたので次にTypescriptの設定を扱います。
    scss同様、以下の必要なプラグインをインストールします。

    npm i -D typescript ts-loader
    • ts-loader
    • Typescriptをjavascriptに変換(トランスパイル)するプラグイン

    webpack.config.jsは以下のようになります。

    webpack.config.js

    const path = require("path");
    module.exports = {
      //省略
      //
      //loaderの設定を記述する場所
      module: {
        //TypeScript や Scssのローダー設定
        rules: [
          {
            //typescriptをjavascriptに変換
            test: /\.(ts|tsx)$/,
            use: [
              {
                loader: "ts-loader",
                },
              },
            ],
          },
          {
            test: /\.(css|scss)$/,
            use: ["style-loader", "css-loader", "sass-loader"],
          },
        ],
      },
    }

    scssの時と同様、testには変換対象のファイルを、useには使用するloaderを設定します。
    また、Typescriptの設定ファイルである、tsconfig.jsonを作成します。
    tsconfig.json

    {
      "compilerOptions": {
        "sourceMap": true,
        "target": "es5",
        "module": "esnext",
        "outDir": "./dist",
        "jsx": "preserve",
        "moduleResolution": "Node",
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "allowSyntheticDefaultImports": true,
        "strict": true
      },
      "include": ["./src"],
      "exclude": ["./node_modules"]
    }

    ts-loaderのおかげで、厳密な型チェックをtsconfig.jsonをもとに行ってくれます。
    以下のようなコードを記述すると、ビルド時にエラーを吐いてくれます。

    const value: number = "Test"
    // 数値に文字列を入れようとすると以下のエラーがはかれる
    // TS2322: Type 'number' is not assignable to type 'string'.

    Babelを扱う

    TypescriptをjavascriptにトランスパイルできてもES5に対応できていなかったら動作しない環境(Internet Exploreなど)があります。
    なので、ES5に対応させるためにBabelを使用します。
    これまで同様必要なプラグインをインストールします。

    npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react

    以下、webpack.config.jsonファイルです。

    webpack.config.js

    const path = require("path");
      //省略
      module: {
        rules: [
          {
            test: /\.(ts|tsx)$/,
            use: [
              //追記
              {
                loader: "babel-loader",
                options: { presets: ["@babel/preset-env", "@babel/react"] },
              },
              {
                loader: "ts-loader",
              },
            ],
          },
          {
            test: /\.(css|scss)$/,
            use: ["style-loader", "css-loader", "sass-loader"],
          },
        ],
      },
    }

    ts-loaderの上にbabel-loaderを追記しました。

    • @babel/preset-env
    • ES6以降のものをES5にトランスパイル
    • @babel/react
    • JSXをJSにトランスパイル

    ここでは、babelで提供されているプラグインを使用し、reactの対応も行っております。

    順序は、
    Typescript(tsx) → javascript(jsx) → js(ES6) → javascript(ES5)
    のような変更です。
    注意
    Babelには、@babel/typescriptプラグインも存在します。
    これはビルド時にTypescriptからjavascriptの変換は行ってくれますが、ts-loaderのように型チェックは行ってくれません。
    (ts-loaderのところで説明したエラーが出ない)
    以上でローダー周りの設定は完了です。

    5.ベースとなるHtml作成

    ベースとなるHtmlを作成します。

    ./dist/index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>Webpack</title>
      </head>
      <body>
        <div id="root"></div>
        <script src="bundle.js"></script>
      </body>
    </html>

    scriptタグで、webpackで1つにまとめたjsファイルを挿入します。(bundle.jsの中にcssも含まれている。)

    6.Scssファイル作成

    Scssを記述します。
    Sassをインストールします。

    npm i -D sass

    今回は、./public/style/style.scssに作成しました。

    ./public/style/style.scss

    .l-index {
      color: red;
      background-color: blue;
    }
    
    

    7.Reactファイル作成

    Reactを記述していきます。
    その前に、Reactで必要なパッケージをインストールします。
    以下の4つです。

    npm i react react-dom @types/react @types/react-dom

    以下のファイルを作成します。

    index.tsx

    import React from "react";
    import ReactDOM from "react-dom";
    import { App } from "../src/App";
    //ここでscssをインポート
    import "../public/style/style.scss";
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById("root")
    );

    App.tsx

    import React from "react";
    
    export const App: React.FC = () => {
      return <h1 className="l-index">カープ優勝</h1>;
    };

    8.動かす

    それでは、実際に画面に「カープ優勝」が表示されるか見てみましょう。

    そのためには、package.jsonの設定を行います。
    以下のようにscriptsの部分にコマンドを設定しておきます。
    package.json

      "scripts": {
        //production環境でビルド
        "build": "webpack --mode production",
        //開発環境でビルド
        "dev": "webpack-dev-server --open",
      },

    それでは以下のコマンドを打ち、動作確認します。

    npm run dev

    自動でlocalhost:3000が開き赤文字で「カープ優勝」が表示されました!



    App.tsxの文字を変更したり、style.scssのスタイルを変更したりして変更がリアルタイムで反映されていたら成功です。

    所感

    今回、weboackをベースにReact + Typescript + Sassの開発環境を作成しました。
    今回の実戦でよりフロントエンドの裏の動きをなんとなく把握できたなと感じます。
    具体的には、以下の2つのことを学びました。

    1. 人が使いやすい言語やフレームワーク(React、Typescript...)をブラウザで動かせる言語や仕様(javascript、css、ECMAScript5)に変換する
    2. 人が管理、開発をしやすいように複数のファイルで開発を行う(モジュール化)が、パフォーマンスを考え1つのファイルにまとめブラウザで動作させる

    以上の学んだことは、今後フロントエンドの流行が変化しても、不変な部分であると感じます。

    参考

    https://ics.media/entry/12140/
    https://ics.media/entry/17376/
    https://zenn.dev/hrkmtsmt/articles/93653309e2a13d
    https://tech.playground.style/javascript/babel-webpack/