16
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

webpackとesbuild-loaderで爆速なReact開発環境を構築しよう

はじめに

フロントエンドのライブラリ・フレームワークの移り変わりって激しいですよね。
webpack5 がリリースされてもう半年と少しくらい経ってしまいました。
最近では新たなフロントエンドのビルドツールに、以前 こちらの記事 に書きました、esbuildvite などのビルドツールもますます盛り上がって来ています。

そういう中、webpack が今後どうなっていくのか少し不安というのがあります。今回は、esbuild から派生した、esbuild-loader を利用することで、webpack でも爆速なビルド環境が構築できるよということを書きますので、そんな不安を少しでも取り除ければいいなと思います。

記事の対象

Node.jsについてある程度理解している、npmなどの環境構築が問題なくできる, webpackの環境構築やなにかしらフロントエンドの環境構築をしたことがある方を対象としています。

準備

以下、必要なモジュールなどをインストールしていきます。

npm i -D typescript ts-node @types/node webpack-cli webpack @types/webpack

これで webpack のコンフィグファイルが TypeScript で記述されてても起動できるようになりました。

以下で必要な loader などをインストールしていきます。

npm i -D esbuild-loader fork-ts-checker-webpack-plugin html-webpack-plugin mini-css-extract-plugin @types/mini-css-extract-plugin postcss-loader sass-loader thread-loader

今回重要なのは、esbuild-loader, fork-ts-checker-webpack-plugin, thread-loader ですので、他は蛇足に近いです。

esbuild-loader

先ほども説明しましたが、esbuild が提供しているAPIを利用して作成された、loader です。抽象構文木解析, 走査周りの処理は、esbuild と同じものなので、よく利用されている ts-loader よりは確実に早いと思います。
esbuildのビルド速度についてはesbuildのドキュメントに記載されています。

fork-ts-checker-webpack-plugin

webpackを利用する際に、TypeScriptのトランスパイルによく利用されている、ts-loader の型チェックの部分のみを切り出したプラグインです。このプラグインを利用することで、
esbuild, esbuild-loader の弱点である、型チェックが行えないという問題を解決することができます。

thread-loader

基本的に、webpackを利用する際には、ファイルの拡張子ごとに loader を分けるなど、細かい設定をすることが多々あります。その際に、この thread-loader を使うことによって、各々の拡張子に対応する loader ごとにスレッドを分割し、マルチスレッドで処理を行うことできます。

thread-loader に関しては、webpack の公式ドキュメントでも紹介されています。
https://webpack.js.org/loaders/thread-loader/

バンドル設定について

今回は例として、React + TypeScript + sass の基本的なバンドル設定を想定して行います。各モジュールのバージョンについては以下の通りです。(2021/04/24時点)

package.json
{
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^5.2.0"
  },
  "devDependencies": {
    "@types/mini-css-extract-plugin": "^1.4.0",
    "@types/node": "^14.14.37",
    "@types/react": "^17.0.3",
    "@types/react-dom": "^17.0.3",
    "@types/react-router-dom": "^5.1.7",
    "@types/webpack": "^5.0.0",
    "css-loader": "^5.2.0",
    "esbuild-loader": "^2.11.0",
    "fork-ts-checker-webpack-plugin": "^6.2.0",
    "html-webpack-plugin": "^5.3.1",
    "mini-css-extract-plugin": "^1.4.0",
    "postcss-loader": "^5.2.0",
    "sass-loader": "^11.0.1",
    "thread-loader": "^3.0.1",
    "ts-node": "^9.1.1",
    "typescript": "^4.2.3",
    "webpack": "^5.28.0",
    "webpack-cli": "^4.6.0"
  }
}

ファイルパスの配置は、以下の通りです。

webpackの設定

以下に webpack の設定ファイルを記述していきます。
わかりづらい箇所は適宜コメントをしています。

webpack.config.ts
const path = require('path')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = () => {
  const IS_DEV = process.env.NODE_ENV === 'development';

  const configs = {
    // ForkTsCheckerWebpackPluginが自動的にtsconfig.jsonを見つけるために必要
    context: __dirname,
    mode: process.env.NODE_ENV,
    devtool: IS_DEV ? 'eval-sorce-map' : 'hidden-nosources-source-map',
    entry: path.resolve(__dirname, 'src/Main.tsx'),
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'bundle.js'
    },
    resolve: {
      modules: [path.resolve(__dirname, 'node_modules')],
      alias: {
        '@': path.resolve(__dirname, './src'),
      },
      extensions: ['.ts', '.tsx', '.d.ts', '.js']
    },
    module: {
      rules: [
        {
          test: [/\.css$/, /\.scss$/, /\.sass$/],
          exclude: /node_modules/,
          use: [
            {
              loader: 'thread-loader',
              options: {
                // worker thread数
                workers: 2,
                // 並列処理の上限数
                workerParallelJobs: 40,
                // キャッシュのmaxサイズ
                workerNodeArgs: ['--max-old-space-size=512'],
                // thread pool名(固有)
                name: 'css-loader-pool',
              }
            },
            { loader: MiniCssExtractPlugin.loader },
            { loader: 'postcss-loader' },
            { loader: 'sass-loader' }
          ],
        },
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'thread-loader',
              options: {
                workers: 2,
                workerParallelJobs: 80,
                workerNodeArgs: ['--max-old-space-size=512'],
                name: 'ts-loader-pool',
              }
            },
            {
              loader: 'esbuild-loader',
              options: {
                loader: 'ts',
                minify: !IS_DEV,
                target: 'es2015',
              },
            }
          ],
        },
        {
          test: /\.tsx$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'thread-loader',
              options: {
                workers: 2,
                workerParallelJobs: 80,
                workerNodeArgs: ['--max-old-space-size=512'],
                name: 'tsx-loader-pool',
              }
            },
            {
              loader: 'esbuild-loader',
              options: {
                // loaderはstring型なのでtsとtsxファイルの設定を別々に定義する必要がある
                loader: 'tsx',
                minify: !IS_DEV,
                target: 'es2015',
              },
            }
          ],
        },
      ],
    },
    plugins: [
      new ForkTsCheckerWebpackPlugin(),
      new HtmlWebpackPlugin({
        filename: 'index.html',
        template: 'src/html/index.html',
      }),
      new MiniCssExtractPlugin({
        filename: '[name].css',
        chunkFilename: '[id].css',
      }),
    ],
  }

  // webpack5以降からはdevtoolsを個別に後から設定する必要がある
  if (IS_DEV) {
    configs.devtool = 'eval-source-map';
  }

  return configs;
}

npmスクリプトも以下のように設定することによって、便利になりそうですね。

package.json
{
  "scripts": {
    "watch": "NODE_ENV=\"development\" webpack --mode development --watch --hot",
    "build-dev": "NODE_ENV=\"development\" && webpack --mode development",
    "build-prod": "NODE_ENV=\"production\" && webpack --mode production"
  },
}

おわりに

今回は、大まかに esbuild-loader を利用した webpack のフロントエンドの環境構築について書きました。みなさんのお役に立つと幸いです。
なにか質問やご指摘あれば、コメントの方によろしくお願いします。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
16
Help us understand the problem. What are the problem?