Mithril+WebPackでフロントを作る一歩目～EHW2018「アプリ①」

# 概要

このエントリは、「Enterprise "hello, world" 2018 Advent Calendar 2018」の12/2向けのものです。このAdvent Calendarでは、複数個のエントリにまたがる話の流れも鑑みつつ、なるべく1エントリで１つのトピックをカバーできるようにする予定です。

このエントリで記載するトピックは、MithrilフレームワークとWebPackで、フロントを作る一歩目を作ることです。

# 前提

## おことわり

- このEnterpfise "hello, world"シリーズは、ネタのためのエントリです。実環境でそのまま利用ことを目的とはしていません。
- 動かしやすさを優先してセキュリティを意図的に低くする設定など入れてありますのでご注意ください。
- このエントリは、フロント側のJSフレームワークを何か一つは触ったことがあって用語はわかる方を想定して記載していますが、そうでなくても流し読みをすると、雰囲気は解ると思います。

## 想定読者

「Enterprise "hello, world" 2018」的なネタとしては、下記のような状況を想定しています。

> 開発環境の整備も結構できたので、~~nginxのLDAP認証もなんだかうまく動かないし後回しにして~~そろそろアプリを書かなくてはいけない。なぜなら15日目だから。

Advent CalendarのDay3で、Day1の昔のプログラムをJavaのコンソールアプリにしましたが、これをベースに、今風のアーキテクチャにしていきたいと思います。
取り掛かりとして、レイヤわけをします。

|![ApplicationModernization.png](https://qiita-image-store.s3.amazonaws.com/0/65002/0dc54989-97f4-7753-dba2-8d86ce5c7362.png)|
|:---:|

このうち、まずはプレゼンテーション層として、フロント部分を作ります。APIはあとから作ることにし、まずはフロント部分だけで最小限の動作ができるものにします。(Enterprise Hello Worldなので、CDNからファイルを落としてきてというお手軽さではなく、少しめんどくさくやります。)

# Mithril

## Mithrilとは

[Mithril](https://mithril.js.org/)は、フロントエンドのための、JavaScriptフレームワークです。本エントリ執筆時点のバージョンは、2.0.0-RC3です。

## 他のフレームワークとの比較

Webのフロントエンドは、ここ数年でいくつかのフレームワークが出現し、日本での流行もいろいろ変わってきています。Mithrilのサイトに、それらのフレームワークとの比較があります。

- [Framework comparison](https://mithril.js.org/framework-comparison.html)

また、Vueのページにも、[Vueとほかのフレームワークの比較](https://jp.vuejs.org/v2/guide/comparison.html)があるので、併せて参照すると参考になります。

本エントリでは、以下の点で、Mithrilを使うことにしました。

- コンパクトで覚えることが少ない
- フロント開発のための最小限の機能が最初から入っている
- ~~__使ったことがないのでこの機会に使ってみたい__~~

## 準備

本家のページに[WebPackと組み合わせた使い方](https://mithril.js.org/installation.html)があるので、その手順に従い使い始めてみます。

### 使っているもののバージョン

Ubuntu18.04のパッケージのものなので、ちょっと古いです。

```
$ node --version
v8.10.0
$ npm --version
3.5.2
```

## プロジェクトを作る

```shell-session
$ npm init --yes
$ npm install mithril --save
$ npm install webpack webpack-cli --save-dev
```

(mithrilのダウンロードは一瞬ですが、WebPackはそれなりに時間がかかります。)

## なにはともあれeslintとprettier

このあたりは何も考えずにやります。

```shell-session
$ npm i eslint prettier standard --save-dev
$ npm i eslint-plugin-prettier eslint-config-prettier --save-dev
```

.eslintrc

```json
{
  "extends": ["prettier", "standard"],
  "env": {
    "browser": true,
    "es6": true
  },
  "plugins": ["prettier", "standard"],
  "rules": {
    "prettier/prettier": [
      "error"
    ],
    "yoda": 0,
    "no-unused-vars": 1
  },
  "globals": {
    "$": false
  }
}
```

## package.json

packge.jsonはこんな感じにしました。scriptsに、WebPackでファイルの変更をすぐにビルドに反映する"start"と、JSを最終的にビルドするための"build"を追加し、Misril付属の"ospec"でテストを実行することを指定しています。

```json
{
  "name": "frontend-mithril",
  "version": "0.0.1",
  "description": "Enterprise hello,world client",
  "main": "index.js",
  "scripts": {
    "build": "webpack src/index.js --output bin/app.js -p",
    "start": "webpack src/index.js --output bin/app.js -d --watch",
    "test": "ospec"
  },
  "keywords": [],
  "author": "Hiroki Ito",
  "license": "MIT",
  "dependencies": {
    "mithril": "^1.1.6"
  },
  "devDependencies": {
    "eslint": "^5.10.0",
    "eslint-config-prettier": "^3.3.0",
    "eslint-plugin-prettier": "^3.0.0",
    "prettier": "^1.15.3",
    "webpack": "^4.28.0",
    "webpack-cli": "^3.1.2"
  }
}
```

## アプリそのもの

本家のチュートリアルに沿って書いたものとほぼ同じものですが、今回用に少し文言を変更したり、テストした結果を踏まえて改変してあるものを以下に掲載します。

SPAなので、index.htmlの中の構造は、JavaScriptで組み立てます。エントリポイントはindex.jsで、その中でMithrilのコンポーネントを一つ指定してマウントしています。

### HTML

```html
<!DOCTYPE html>
<html>
  <head>
    <title>Enterprise "hello,world"</title>
    <meta charset="UTF-8" />
  </head>
  <body>
    <script src="bin/app.js"></script>
  </body>
</html>
```

### js

#### index.js

```js
var m = require("mithril");

var HelloWorldComponent = require("./helloworld-component");

m.mount(document.body, HelloWorldComponent);
```

#### helloworld-component.js

```js
var m = require("mithril");

module.exports = {
  view: function() {
    return m("div", m("p", "hello, world"));
  }
};
```

## 開発時のビルド

WebPackでファイルの更新を監視しながら、ビルドするように、下記を動かします。

```
$ npm run start
```

Visual Studio Codeの統合ターミナルで動かしている様子です。

![Screenshot_2018-12-21_05-21-23.png](https://qiita-image-store.s3.amazonaws.com/0/65002/fd89fcf8-e93f-bfba-52ad-e96aade0830a.png)

## 動作確認(WebPackでビルド)

Firefoxで開いてみます。動いてますね。

![Screenshot_2018-12-21_05-26-50.png](https://qiita-image-store.s3.amazonaws.com/0/65002/072f1c03-4b85-8165-1f80-45d6dc20faaf.png)

## 単体テスト(ospec利用)

Mithrilには、テストを実施するために「[ospec](https://mithril.js.org/testing.html)」というテストフレームワークが最初からついています。これを使ってテストを書いてみます。

```js

const o = require("mithril/ospec/ospec");
const callAsync = require("mithril/test-utils/callAsync");
const mock = require("mithril/test-utils/browserMock")();

global.window = mock;
global.document = mock.document;

var HelloWorldComponent = require("../src/helloworld-component");

o.spec("HelloWorldComponent", function() {
  o("returns a div", function() {
    var vnode = HelloWorldComponent.view();

    o(vnode.tag).equals("div");
    o(vnode.children.length).equals(1);
    o(vnode.children[0].tag).equals("p");
    o(vnode.children[0].text).equals("hello, world");
  });
});
```

```
$ npm run test
```

テストが成功し、数のような表示となります。

![Screenshot_2018-12-21_05-31-27.png](https://qiita-image-store.s3.amazonaws.com/0/65002/5d5fadff-852a-78dd-cfed-2360536f9eda.png)

## リリース版ビルド(WebPackでビルド)

さて、ここまでの時点では、WebPackのビルドはデバッグモードなので、ビルドしてできたapp.jsのサイズは、172KBと、とても大きいです。(mithril自体のフットプリントは8KB)

![Screenshot_2018-12-21_05-36-19.png](https://qiita-image-store.s3.amazonaws.com/0/65002/b9220671-43c6-da2d-16cc-bcdd04bdd14d.png)

これを小さくします。

```
$ npm run build
```

![Screenshot_2018-12-21_05-39-12.png](https://qiita-image-store.s3.amazonaws.com/0/65002/90b64f1e-fe91-826a-d482-bbbf88f3a56d.png)

結果として、WebPackによりJavaScriptのバンドルとminifyが実施され、28KB程度にまで小さくなりました。

![Screenshot_2018-12-21_05-41-11.png](https://qiita-image-store.s3.amazonaws.com/0/65002/b3a38eb3-1832-c98e-8ad7-7eb6c3470e87.png)

中身はこんな感じです。

![Screenshot_2018-12-21_05-43-06.png](https://qiita-image-store.s3.amazonaws.com/0/65002/fa168b19-3e46-ddd7-44a4-3de4ca726d70.png)


# まとめ

このエントリでは、「Enterprise "hello, world" 2018 Advent Calendar 2018」(EHW2018)の15日目として、MithrilフレームワークとWebPackで、フロントエンド作る一歩目をトピックとして取り上げました。

本エントリのソースコードは、[https://github.com/hrkt/frontend-mithril/releases/tag/0.0.1](https://github.com/hrkt/frontend-mithril/releases/tag/0.0.1)にあげてあります。

EHW2018のネタとしては、このあと、API側を作ることを考えています。