Webpack

開発用と本番用で設定ファイルを分ける

https://webpack.js.org/guides/production/

基本的な設定

package.jsonのスクリプト

{
  "scripts": {
    //
    // --open: ブラウザを自動で開く
    // --hot: ホットリロード(HMR)を有効にする
    "start": "webpack-dev-server --mode development --open --hot",

    // --config: configファイルを指定する
    "build": "webpack --mode development --config webpack.config.prod.js"
  }
}

sassを使う

yarn add -D sass-loader node-sass style-loader css-loader

webpack.config.js

useの中に書いたものは後ろから順に適用される。各loaderがやっていることは以下の通り。

  • sass-loader: .scssファイルを.cssファイルに変換する
  • css-loader: .cssファイルの@importを解決したりクラス名のローカルスコープ化などを行う
  • style-loader: スタイルをjsファイルに埋め込む
module.exports = {
	...
    module: {
        rules: [{
            test: /\.scss$/,
            use: [
                "style-loader",
                "css-loader",
                "sass-loader"
            ]
        }]
    }
};

postcssを使う

yarn add -D postcss postcss-import postcss-loader postcss-nested cssnano

postcss.config.js

module.exports = {
  plugins: [require('postcss-import'), require('postcss-nested'), require('cssnano')({ preset: 'default' })],
}

webpack.config.jsをTypeScriptで書く

Ref: https://webpack.js.org/configuration/configuration-languages/#typescript

TypeScript + postcss

package.json

{
  "scripts": {
    "start": "webpack-dev-server --mode development --open --hot",
    "build": "webpack --mode production"
  },
  "devDependencies": {
    "awesome-typescript-loader": "^5.2.1",
    "css-loader": "^2.1.0",
    "cssnano": "^4.1.8",
    "html-webpack-plugin": "^3.2.0",
    "postcss": "^7.0.7",
    "postcss-import": "^12.0.1",
    "postcss-loader": "^3.0.0",
    "postcss-nested": "^4.1.1",
    "style-loader": "^0.23.1",
    "tsconfig-paths-webpack-plugin": "^3.2.0",
    "typescript": "^3.2.2",
    "webpack": "^4.28.3",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.14"
  }
}

postcss.config.js

module.exports = {
  plugins: [ require("postcss-import"), require("postcss-nested"), require(
        "cssnano"
      ) ({preset: "default"}) ];
}

webpack.conf.js

const path = require("path");

const HtmlWebpackPlugin = require("html-webpack-plugin");
const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");

module.exports = {
  entry: "./examples/index.tsx",
  resolve: {
    extensions: [".ts", ".tsx", ".js"],
    plugins: [new TsconfigPathsPlugin({ configFile: "./tsconfig.json" })]
  },
  output: {
    path: path.join(__dirname, "/dist"),
    filename: "bundle.min.js"
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "awesome-typescript-loader"
      },
      {
        test: /\.css$/,
        use: [
          "style-loader",
          { loader: "css-loader", options: { importLoaders: 1 } },
          "postcss-loader"
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./examples/index.html"
    })
  ]
};

TypeScript

yarn add -D ts-loader

webpack.config.js

module.exports = {
  mode: 'development',
  entry: './src/main.ts',
  module: {
    rules: [
      { test: /\.ts$/, use: 'ts-loader'}
    ]
  },
  resolve: {
    extensions: ['.ts', '.js']
  }
};

typescriptだけで書く場合も、読み込むライブラリが.jsをimportしている場合があるためextensions.jsは必須。

{
  "compilerOptions": {
    "sourceMap": true,
    "target": "es5", // TSはECMAScript 5に変換
    "module": "es2015" // TSのモジュールはES Modulesとして出力
  }
}

ビルド時間

できるだけ階層深くimportする

// NG
import { Menu } from '@material-ui/icons';

// OK
import Menu from '@material-ui/icons/Menu';