八宝菜は中華料理です

八宝菜という中華料理がこのブログを書いています。

30分ちょっとの時間と慣れと気合で作るフロントエンド開発環境

初投稿です。
シャカイに出て1年とちょっとが経ちました。研修後に配属されたチームで、なんとあれほど避けていた フロントエンド をやるハメになりました。
大体ここ10ヶ月ぐらいで学んだり感じたりしたことをここに書いてみようと思います。


目次

  1. エディタのインストー
  2. node.jsのインストー
  3. npmを使ってみる
  4. webpackのインストー
  5. babel及びプラグインのインストー
  6. webpack-dev-serverのインストー

目標:
基本的なnode.js, npmの仕組みを使ってフロントエンドの 最小限の 開発環境を構築する。

エディタのインストー

メモ帳以外ならなんでもいいです。
僕はVisual Studio Codeを使ってます。
理由としては、JavaScriptの補完が大変優秀だったり、エディタ上で端末も起動できるので、node.jsを扱う上で大変相性がよかったり、などでしょうか。
欠点としてはちょっとメモリ大食いなところ…まあ、最低限の人権が確保されている環境ならメモリは32GB積んでて当たり前なので関係ないですよね
ぶっちゃけ、このへんは宗教好みなのでご自由にどうぞ。ブルジョアならWebstormとかの有料IDEがいいんですかね?

node.jsを入れる

はい、フロントエンドって言ってるのにサーバーサイドで動くっぽいやつを入れる時点でもう不穏ですね。まあ一旦落ち着いて、UNIXな人はyumやらaptでnode.jsをinstall、Windowsな皆さんはGoogleでnode.jsと検索して最新っぽいやつを入れましょう。
node.js自体のバージョンを切り替えなければならないことも仕事だとあるんですが、個人のマシンの上でやるなら必要ないと思いますし、あとからバージョン管理ツールを導入するのも別に問題ないので今回は特に何も考えずに最新版を入れましょう。現時点だと6.10.3が最新版らしいです。

node.jsがインストール出来たら、端末でnodeと打ってみましょう。REPL(コマンドラインでコード打ち込むと実行してくれるアレ)が起動したらインストールは成功です。コマンドがないよって言われる人は再起動。
f:id:happo31:20170510222858p:plain
PowerShellなのは気にしない 。VSCode使ってるんだからそっちのスクショ取ればよかったが時既に遅し。。。

npmを使ってみる

n ode p ackage m anagerの名前の通り、node.jsで動いたり動かなかったりするライブラリを管理するためのツールです。
動かないと書いたのは、ブラウザ上で動かすためのフレームワークなんかも入れられたりするからです。

$ mkdir hello_node
$ cd hello_node

ディレクトリを掘ります。
よし(ユーザーディレクトリに)ぶち込んでやるぜ! という男気あふれる人は飛ばしてもいいです。
次に、パッケージを管理できるようにpackage.jsonというのを生成します。僕もハマったことがあるんですが、このjsonがないとnpmはただこのディレクトリにモジュールをぶち込むことだけしかしてくれません。
その場合、このプロジェクトを配布したりgitで管理したいというときに、どこかに「これとこれとこれとこのライブラリはこのバージョン入れてね」というのを書いておかないといけません。
それはちょっと困るので次のコマンドを打ってみましょう。あ、nodeが起動したままだとまずいのでCtrl+C Ctrl+Cで抜けてくださいな。

$ npm init

f:id:happo31:20170510224849p:plain こういう感じの対話型ツールが起動します。
ここではプロジェクトの名前が設定できるんですが、こだわりがなければそのままEnterで押しましょう。何も入力しないと()内のデフォルトの名前が使われます。
続いてversion, description, entry point, test commandなどが聞かれますが、あとで設定すればよいのでEnterを連打してもOK。

$ npm init -y

と-yオプションを付けると、実はこの対話を全部スキップ出来ます。こっちのほうが便利かも。
さて、そうして出来上がったpackage.jsonがこちら。

{
  "name": "hello_node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

なんかごちゃごちゃしてますが最初は特に知らなくても使えます。使い込んでいくと嫌でも知ることになりますが。
さて、ここまででようやくnpmでパッケージが管理できるようになりました。手間取らせやがってという感じが早速出て参りましたが、慣れてしまえばここまでは1分くらいでできると思います。ほら、タイトルにも慣れって書いてあるし多少はね?
あ、このディレクトリをgitで管理したい、という人はここまででgit initしておくとよいです。
このあとnode_modulesという、インストールしたパッケージがまるまる入ったディレクトリが出来るんですが、ここをgitで管理してしまうと大変なことになることは想像のとおりです。
さっさと.gitignoreまで作ってしまいましょう。node_modules/とか一行目に書いておくと吉。 さて、それではいよいよnode packageをインストールしてみましょう。

npm install --save react

ゲェーReactかよ!!と思った人、これは例なので今回はReact使いません。
これを打つと、なにやらずらずら動いていろんなものがたくさんnode_modulesに入りますが、ここでちょっと package.jsonを覗いてみましょう。

{
  ...
  "dependencies": {
    "react": "^15.5.4"
  }
}

こんな行が追加されていると思います。
これがnpmのキモです、このdependencies(依存)というプロパティに、 このプロジェクトが依存しているパッケージモジュールの情報とバージョン が記録されていきます。何が嬉しいのかを試すため、一度node_modulesディレクトリを削除してみましょう。

$ rm -rf node_modules/

さっきインストールしたreactは跡形もなく消え去りました。 気持ちええんじゃ
次に、さっき打ったコマンドの、何も付いてないバージョンのようなコマンドを実行します。

$ npm install

すると、先程入れたパッケージがnode_modulesに復元されました。よいですね、これがpackage.jsonの楽しいところです。

dependenciesプロパティの"react": "^15.5.4"は、reactというパッケージのバージョン15.5.4以上、ということを表しています。
npmはここを見て、 パッケージ名とそのバージョンから自動でnode_modulesへパッケージをインストールしてくれる、というわけです。

gitで管理する上でも、このpackage.jsonの更新をコミットしておくことで、他の環境でもnpm installするだけで開発環境が整うわけですね。すっごーい!
バージョンに変更があった場合なども問題なく再インストールされますので、ご安心を。

実はこのdependenciesには--saveではなくて--save-devを指定した場合に出来るdevDependenciesというプロパティもあって、ちゃんと使い分けないといけないんですが、本質ではないのでどういう時に使い分けをするのかはここでは省略します。ググって❤

Webpackを入れる

間髪入れずに次のコマンドを打ちます。

$ npm install webpack --save-dev

せっかくなので--save-devを使ってみました。
Webpackとは、JavaScriptの加工に特化したビルドツールです。設定すれば難読化を掛けて読みづらくしたり、空白や改行を削ってサイズを小さくするminifyという作業も自動でやってくれるようになったりします。ややこしくなるので今回はそこは扱いません。

単体ではあまり意味がないので、次のwebpack用プラグインもインストールします。プラグインとは言え、やることはパッケージと変わりません。
ちなみに、npmはこのようにインストール時にパッケージ名を複数個まとめて書くことで、一気にパッケージをインストールすることが出来ます。

npm install babel-core babel-loader babel-preset-es2015 --save-dev

Babelとは、ある時期のバージョンで書かれているJavaScriptをいい感じにブラウザで動くJavaScriptに変換してくれるいわゆる トランスパイラ です。コンパイラとはちょっと役割が違うのでこう呼びます。-loaderと付いているのは、Webpackで動かすためのプラグインとして提供されているものを入れているからです。

babel-preset-es2015は、現在2017/5/11時点でのJavaScriptの最新バージョンである ECMAScript2015 で書かれたコードを、ブラウザで確実に動くバージョンである ECMAScript5 へ変換するための設定のプリセットです。Babelは、適用する言語ルールを自由にカスタマイズ出来るので、よくわからない場合はこういったプリセットを使うと良いらしいです。(この辺は僕もあまり使ったことがないのでよく分からずに書いてます。あとはググって❤)
さて、ここからはちょっとヘビーです。設定ファイルを書く必要があるからです。
webpackは動作させるのに設定ファイルを書いておくと便利なので、最初から書いてしまいます。とは言え、1から書くのは面倒なのでテンプレートを用意しました。まるで3分クッキング。

module.exports = {
    entry: "./src/index.js", // プログラムのエントリーポイント
    output: {
        path: __dirname + "/dist/", // ファイルの出力先
        filename: "app.bundle.js" // 生成するファイル名
    },
    module: {
        loaders: [
            {
                test: /\.js$/,  // 読み込むファイルのマッチ条件。ここではjsファイルを指定
                loader: "babel-loader" // 読み込んだファイルを渡すプラグイン名
            }
        ]
    }
};

これをwebpack.config.jsというファイル名で保存してください。使うファイル名はなんでも構いません、あとでファイル名指定で読み込ませるので。
次に、babelのための設定ファイルを用意します。こちらも書いておきました。とは言えたったの3行ですが。

{
  "presets": ["es2015"]
}

逆にこっちは.babelrc というファイル名で保存してください。先頭のドットを忘れずに。Windows雑魚なのでドットで始まるファイルはエクスプローラーからだとエラーが出て作成できなかったと思うので、

type nul > .babelrc

とかで一旦空のファイルを作ってからやるといいかも。

さて、ここでpackage.jsonに戻り、scriptsプロパティが書いてある辺りに次のように書きます。 元々あった"test"という行は消して構いませんゾ。

...
"scripts": {
  "build": "webpack --config ./webpack.config.js --progress --color --watch"
},
...

このscriptsプロパティは、npm run <command>というnpmコマンドで起動するコマンドを書くことが出来ます。
つまり、この行ではnpm run buildコマンドでwebpackを「configファイル有り」「途中経過の表示をON」「カラーで表示」「ファイル監視モード」という設定で起動する、という意味になります。(webpackのオプションについてはググって)
こんな感じで長いコマンドもpackage.jsonでスマートに管理出来るので結構有用です。
さて、せっかくなので起動してみます。

$ npm run build

以下のような出力が得られます。 f:id:happo31:20170511010101p:plain
おや、何やら怒られていますね。それもそのはず、webpackの設定ファイルによればsrc/の下にindex.jsがあるはずなのに、まだ作っていないからです。
さくっと作りましょう。

$ mkdir src
$ cd src
$ touch index.js

index.jsの中身は、ES2015の書き方をちょいと取り入れたかったので今回はこんな感じにしてみました。

// クラス!!!
class Student {
    constructor(name, age) {
        this._name = name;
        this._age = age;
    }
    // プロパティ!!!
    get name() {
        return this._name;
    }

    get age() {
        return this._age;
    }
}

window.onload = () => { // ラムダ式!!!
    let yamada = new Student("yamada", 114514);
    const h1 = document.createElement("h1");
    // 文字列への変数埋め込み!!!
    h1.innerText = `${yamada.name} is ${yamada.age} years old.`; 
    document.getElementById("app").appendChild(h1);
};

んで、こいつを読み込んで表示するためのindex.htmlはこちらです。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <title>Hello Node</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
        <div id="app">
        </div>
        <script src="./dist/app.bundle.js"></script>
    </body>
</html>

以下のコマンドを実行すると、webpackが走ってdist/にapp.bundle.jsを生成してくれます!

$ npm run build

index.htmlをウェブブラウザで開きます。

$ chrome ./index.html

f:id:happo31:20170512013656p:plain
ヤッター!!!
あと、webpackには変更を監視して自動でデプロイしてくれる機能もあるので、いちいちコマンドを再実行をしなくても勝手にやってくれます、こんな感じです。

    let yamada = new Student("yamada", 1919);

保存してウェブブラウザを更新すると↓ f:id:happo31:20170512014648p:plain
気持ちがいい。

Webpack-dev-serverを入れる

さて、一応スクリプトは動くのでこのまま作っていくことは出来るっちゃ出来るんですが、それだとajax使ったアプリとかが動かないので、例えばネットワークとギュンギュンするようなウェブアプリは作れません。
apacheだのを建ててそこにビルドしたファイルを配置するようにwebpackを設定してもいいんですが、せっかくのnode.jsなのでwebpack-dev-serverというのを使ってみましょう。
名前の通り、これはwebpackと連携して動くHTTPサーバーです。
例によって以下のようにインストールします。

$ npm install webpack-dev-server --save-dev

ちょっとややこしくなりますが、webpack.config.jsに以下の様に追記

// プラグインの読み込み
const { HotModuleReplacementPlugin } = require('webpack');

module.exports = {
    entry: "./src/index.js", // プログラムのエントリーポイント
    output: {
        path: __dirname + "/", // ファイルの出力先
        filename: "app.bundle.js", // 生成するファイル名
        publicPath: "dist/" // devServerにapp.bundle.jsを置いて欲しいディレクトリ
    },
    devtool: "source-map",
    module: {
        loaders: [
            {
                test: /\.js$/,  // 読み込むファイルのマッチ条件。ここではjsファイルを指定
                loader: "babel-loader", // 読み込んだファイルを渡すプラグイン名
                query: {
                    presets: ["es2015"]
                },
            }
        ]
    },
    devServer: { // devServer用の設定プロパティ
        contentBase: "./",
    },
    plugins: [
        new HotModuleReplacementPlugin()
    ]
};

あと、このwebpack-dev-serverを起動するための起動スクリプトも書きます、やれやれ。

...
  "scripts": {
    "build": "webpack --config ./webpack.config.js --progress --color --watch",
    "start": "webpack-dev-server --hot --inline"
  },
...

以上のような巨大な設定ファイルを書いてようやく開発サーバーの起動が出来ます。

$ npm start

startはnpmにもともと用意されているコマンドで、startという名前のコマンドを自動で起動してくれます、要はrunを省略できるということです。

http:localhost:8080/index.htmlを開き、先程普通のwebpackを起動したときと同じように表示されたかを確認します。(たぶん出来てると思います)
ポート番号を変えたいという場合は、

...
  devServer: {
     contentBase: "./",
     port: 11451, // ここ
  },
...

とかでいいはず。

さて、これは一つハマリポイントなんですが、実はこの webpack-dev-server、実際には distディレクトリにjsファイルを出力しません。
どういうことかというと、メモリ上にjsファイルを展開することで、応答性を高めているとかなんとか…。この辺は正確な理由は僕自身もよく知らないので、、、まあでもなんとなくいいことがありそうですよね。
メモリ使用量は増えそうですが、せいぜい数MBとかだと思いますし。
まあ、最低限の人権が確保されている環境ならメモリは32GB積んでて当たり前なので(2回目)
というわけなので、実際に出力ファイルが欲しい場合は改めてnpm run buildを実行する必要が有ることは、頭に入れておいてください。
お疲れ様でした!

終わりに

はい、というわけで今回はここまでとなります。
今回作ったものはgithubにて公開しているので、cloneしてnpm startするだけで試すことが出来ます。

github.com

あくまでもこれは 最小構成 です。
実際にはJavaScriptで書きたくないしTypeScriptで書きたい~とか、ReactとかAngularとかvue.js使ってイケメンフロントエンド開発がしたい~のような感じだと思います。
でもそんなときにこの記事を振り返れば、いきなりよく分からない設定ファイルや概念が出てきても対応できるかと思います。
まあ、あくまでnode.jsがこのまま仕様を変えずにいてくれたら、の話ですが。
大事なのはトレンドを追い続けることではなく、プロダクトに最適な構成を取捨選択出来るようになることだと思います。

ここまで読んでくださりありがとうございました。
指摘などあればぜひコメント欄によろしくお願いします。