webpack & node-sass & eslintでシンプルなフロント開発環境構築

webpack & node-sass & eslintでシンプルなフロント開発環境構築

目的

  • Web Framework で使うことを前提としてES6, Sassをトランスパイルする。
  • フロント開発に必要な人権を満たす開発環境を作る。

指針

  • フロントエンドをcreate-react-app, vue-cliなどのツールに頼らずに0ベースで作る。 -> 拡張性の問題(変な依存、余計な設定を盛り込みたくない。cli 使っても結局 Document 読むから面倒臭い)
  • 多人数の開発チームを想定して構築する。
  • シンプルが正義。

経緯

Spring bootで個人開発していたが、やっぱりプレーン JS は書きたくなかった。 それと今までcreate-react-appでごちゃごちゃやっていたので、1 からフロント環境を設定したかった。 著者は執筆時点で実務 1 年ちょいのフロント知識浅めなペーペーなのでマサカリお願いします。

要件・設計

  • yarn buildコマンドで js のバンドルファイルと Scss のトランスパイル結果をデプロイ先のフォルダに配置する。
  • yarn devコマンドで JS, css のホットリローディングに対応する。
  • メディアクエリーを css に書きたくないので、SP と PC の css ファイルを分離しておく(そうでないケースもあると思います)。

実装

自分はspring bootの static フォルダを資源の配置先にしているので、そうでない方は読み替えてください。 なお、node_modulesについて開発環境でしか使わないものはyarnなら-Dオプション。npmなら--save-devオプションをつけるようにしてください。

webpack

yarn add -D webpack webpack-cli
module.exports = {
  mode: process.env.NODE_ENV || 'development',
  // バンドル対象のファイルのエントリーポイント
  entry: ['./src/index.js'],
  output: {
    // ビルド後のファイル名
    filename: 'bundle.js',
    // ビルド後の配置先ディレクトリ
    // webアプリにビルド結果を配置
    path: __dirname + '../../src/main/resources/static/js'
  },
  module: {
    // どのファイルに対してどんなことをするか
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['@babel/preset-env']
        }
      }
    ]
  }
};

次に、設定したようにトランスパイルするため、以下のようにyarn addnpm installしてください。

yarn add -D @babel/core @babel/polyfill @babel/preset-env babel-loader

これでsrc/indexファイルがwebpack --config webpack.config.jsを実行すると指定先にコンパイルされたバンドルファイルが生成される。便利! ※src/indexで import しているファイルがバンドル対象になるので、他の設定は不要です。

ESLint

cli ツールに頼らないと言いつつもいつも初期化するときは cli でやっていたので、

yarn add -D eslint

して

npx eslint --init

npx コマンドはnpm initされた階層のnode_modulesの cli モジュールを PATH を通さなくても呼び出せる、そんなノリのコマンドっぽい。

で対話形式で設定ファイルを作成する。自分は JSON で作成して以下の通りになった。

{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": ["eslint:recommended", "plugin:vue/essential"],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "plugins": ["vue"],
  "rules": {
    "semi": "error",
    "quotes": ["error", "single"]
  }
}

ESLint のエディタ設定は VScode のみ次回更新。

node-sass

ついでに sass を使いたかったのでnode-sassの cli ツールで.scss.cssにトランスパイルする。

yarn add -D node-sass

webpackでもできそうだが、このコマンドで必要なことはできるので採用。

npx node-sass [トランスパイル対象ファイル] [出力先]

とりあえずここまでで必要なことは全て揃っている。 そしたらあとはnpm scriptsの設定をする。 なお、npm scriptsでスクリプトを書くことになるわけだが、このスクリプトに環境依存を持たせないために、今回追加でnpm-run-allモジュールを追加する。

npm-run-all とは

以下がここまで依存関係を追加した結果と、npm scriptsの設定になる。

{
  "name": "client",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "dev": "run-p run:js run:sass",
    "build": "run-s build:js; yarn build:sass",
    "build:js": "webpack --config webpack.config.js",
    "run:js": "webpack --progress --colors --watch --config webpack.config.js",
    "build:sass": "run-s build:sass:pc build:sass:sp",
    "build:sass:pc": "node-sass src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "build:sass:sp": "node-sass src/style/sp/index.scss ../src/main/resources/static/css/sp.css",
    "run:sass": "run-p run:sass:pc run:sass:sp",
    "run:sass:pc": "node-sass -w src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "run:sass:sp": "node-sass -w src/style/sp/index.scss ../src/main/resources/static/css/sp.css"
  },
  "dependencies": {},
  "devDependencies": {
    "@babel/core": "^7.8.6",
    "@babel/polyfill": "^7.8.3",
    "@babel/preset-env": "^7.8.6",
    "babel-core": "^6.26.3",
    "babel-loader": "^8.0.6",
    "babel-preset-es2015": "^6.24.1",
    "eslint": "^6.8.0",
    "node-sass": "^4.13.1",
    "npm-run-all": "^4.1.5",
    "webpack": "^4.41.6",
    "webpack-cli": "^3.3.11"
  }
}

重要なのはここ。

"scripts": {
    "dev": "run-p run:js run:sass",
    "build": "run-s build:js; yarn build:sass",
    "build:js": "webpack --config webpack.config.js",
    "run:js": "webpack --progress --colors --watch --config webpack.config.js",
    "build:sass": "run-s build:sass:pc build:sass:sp",
    "build:sass:pc":"node-sass src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "build:sass:sp": "node-sass src/style/sp/index.scss ../src/main/resources/static/css/sp.css",
    "run:sass": "run-p run:sass:pc run:sass:sp",
    "run:sass:pc": "node-sass -w src/style/pc/index.scss ../src/main/resources/static/css/pc.css",
    "run:sass:sp": "node-sass -w src/style/sp/index.scss ../src/main/resources/static/css/sp.css"
  }

コマンドの内容は雰囲気でわかると思うので、軽く説明。

  • JS のホットリローディング
# ファイル更新のたびにビルドが走り、コンパイルの進捗も表示 + 色付け
webpack --progress --colors --watch
  • Scss のホットリローディング
# src/style/pc/index.scssをファイル更新のたびに
# ../src/main/resources/static/css/pc.cssにトランスコンパイル
#
node-sass -w src/style/pc/index.scss ../src/main/resources/static/css/pc.css
  • npm-run-all のコマンド
# 1と2を並列で実行
run-p [npm scripts1] [npm scripts2]

なお、ビルド時には失敗したならすぐに処理落ちして欲しいのでシーケンシャルに実行する。 ここはケースバイケースでいいと思う。

# 1と2を直列で実行
run-s [npm scripts1] [npm scripts2]

Example

自分の場合は最終的にこうなった。

|--.eslintrc.json
|--package-lock.json
|--package.json
|--src
|  |--index.js
|  |--style
|  |  |--_define.scss
|  |  |--pc
|  |  |  |--index.scss
|  |  |--sp
|  |  |  |--index.scss
|--webpack.config.js
|--yarn.lock
--resources
|  |  |  |--application.properties
|  |  |  |--static
|  |  |  |  |--css
|  |  |  |  |  |--normalize.css
|  |  |  |  |  |--pc.css
|  |  |  |  |  |--sp.css
|  |  |  |  |--js
|  |  |  |  |  |--bundle.js
|  |  |  |  |--test.html

ビルドを実行すると狙った通りの箇所に配置できていることがわかる。

<!-- for check js and css files without application server -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link href="css/normalize.css" rel="stylesheet"></link>
    <link rel="stylesheet" type="text/css" media="only screen and (min-width: 0px) and (max-width: 480px)" href="css/sp.css">
    <link rel="stylesheet" type="text/css" media="only screen and (min-width: 481px)" href="css/pc.css">
    <title>Document</title>
</head>
<body>
    <h2>title</h2>
    <div id="header">header</div>
    <div class="body">body</div>
    <script src="js/bundle.js"></script>
</body>
</html>

まとめ

やりたいことが他にあったので端折りまくってしまいましたが、所詮備忘録程度に見ていただければ幸いです。