使いまわしを想定したWebアプリプロジェクトを考える

この記事は [FUJITSU Advent Calendar 2017 part2](https://qiita.com/advent-calendar/2017/fujitsu_part2) の13日目です。

## 最近困っていたこと

最近、デモ用のWebアプリを作る機会が増えてきて、以下のことに困っていました。

1. アプリをシンプルに作ったつもりが、使いまわしを考慮してないので、別件を頼まれたときに、新規にアプリケーションプロジェクトを作る羽目になる。
    * そのため
      * アプリとしての体裁を整えるまでに時間がとられ、見た目含めクオリティが上がらない。
      * 作り方を思い出すまでに時間がかかる。
      * 新しいことを試したい欲求にかられる。
2. 他の人が作ったときのクオリティにばらつきが出る。
3. デモアプリなので、内部のコードは汚くていいという心理が働く。

それぞれ言い訳をする。

1. 突発的に頼まれたりしたときに、後先考えずに作ってしまう。
    * 新規にプロジェクトを作って、ある程度アプリとしての体裁を整えるまでが時間がかかる。一番面倒。
    * 前に作ったのをコピペしただけのはずなのに動かないことがある。
2. 製品ならば、引継ぎ資料などを作りますが、デモ用なので、個人技でホイっと作ってしまう。
3. 自家製の~~汚い~~ソースを作ってしまい、後で誰かに渡すのが申し訳ないと考えてしまう。


## 改善したいポイント

* 毎回、レベル１からのスタートはつらい。せめて、"強くてニューゲーム"を  
⇒　**プロジェクトをcloneし、コマンドを実行(npm installとgulp)するだけで、最低限の動きは保証。作り込みたい場所、差し替えればよい場所を誘導するアプリケーションプロジェクトを布教する。**
  * 同時に以下にも対応。
      * 新しいことを試したい欲求。
      * 汚いコードの除去。

> +α(ちょっとした願望) 新人にも少しずつ触れてもらい、Webアプリに慣れていってほしい。

// 残りの課題について。

* 人間なので、忘れるのは仕方がない。

以上から、自分なりに使いまわしを想定したプロジェクトを考えてみた。

## 準備

* Node.js・・・stableで良い。
  * gulpとbrowser-syncはインストール済みであること

```sh
$ npm install -g gulp browser-sync
```

> npm install 前に、Proxy設定を確認。[参考](https://qiita.com/LightSpeedC/items/b273735e909bd381bcf1)

## 技術選定

モバイル利用も想定して、SPAにすること前提で選定します。
ターゲットは、普段Webアプリを書いてない人。

* フレームワーク
  * [Riot.js](http://riotjs.com/) 
      * 今回、v3.7.4を利用
      * SPAの設定が簡単
      * タグベースのコンポーネント指向
      * scoped cssがある
      * ファイルが軽い
      * ドキュメントが日本語対応してる
* HTMLテンプレートエンジン
  * [pug](https://pugjs.org)
      * ファイル分割と、HTML/CSS/JSをincludeできる。
      * 記述量が減らせる。
          * もし、Pugが覚えられず、htmlで書いても許される。(しつこいですが、includeだけは使ってください。ごちゃまぜでも大丈夫）
      * ゴリラでもわかる
          * [【Pug】ゴリラでもわかるJade改めPug入門](https://blog.mismithportfolio.com/web/20160326pugbegin)
* タスクランナー
  * [gulp](https://gulpjs.com)
      * pugの実行や集約を任せる
      * 各Alt系のビルド（必要なら）

> テスト関連の話は今回触れません。

## ビルドタスクの全体イメージ

<img src="https://qiita-image-store.s3.amazonaws.com/0/213796/3aa32f71-ef81-a0fc-c380-312a6a2ac53d.png" width="450px;" />

画像のように、PostCSSやTypeScriptなど、Alt系をビルドしてから、Pugのincludeを利用し、CSSとJavaScriptを取り込んだHTMLを生成します。（Alt系を使わない場合は、真ん中の列（Pug+CSS+JS）だけやればよいです。今回、サンプルで紹介するソースにはpostcss/typescriptは出てきません。）

> pugをビルドすると、デフォルトで拡張子がhtmlになります。拡張子を変えたい場合は各自変更してください。筆者は.tagに変更し、`dist/tag/`にコピーしてます。


## プロジェクト構成

以下のようにしてみました。

```text
<project_root>/
      ┣━assets/        ライブラリや変更を加えないファイルを格納
      ┣━dist/          ビルド後の資産格納
      ┃  ┣━css/
      ┃  ┣━tag/        pugをビルドした後のファイルを格納
      ┃  ┣━js/
      ┃  ┗━index.html
      ┣━node_modules/  使用するnode_moduleを格納
      ┣━src/           変更を加えるファイルを格納
      ┃  ┣━css/        postcsファイルの格納
      ┃  ┣━pug/        pugファイルのみ格納
      ┃  ┣━ts/         TS or JSファイルの格納
      ┃  ┗━index.html  開発序盤のライブラリの入れ替えを想定。src直下に置く。
      ┣━tmp            postcss/tsのビルド後の資産を一時格納
      ┃  ┣━ css/       ビルド後のCSS一時格納
      ┃  ┗━ js/        ビルド後のJS一時格納
      ┣━ .gitignore
      ┣━ gulpfile.js
      ┗━ package.json
```

pugのビルド後、ビルド後資産格納領域`dist/`に格納、この時、`assets/`に入れた、ライブラリもコピー・格納します。

格納後、サーバーを起動、テスト環境なら、[browser-sync](https://www.browsersync.io)を使いましょう。
ライブリロードを活用した開発はオススメです。[参考URL:Browsersyncを利用してお手軽ブラウザ確認環境をつくろう](http://tech.medpeer.co.jp/entry/2015/06/09/071758)

```js

//browser-syncの起動
gulp.task('setupBrowserSync', function () {
    browserSync({
        server: {
            baseDir: "./dist/"
        }
    });
});

// src/以下の資産の変更を検知すると、ビルドして、参照しているブラウザをリロードします。

gulp.task('watch', ['prepareBuild','setupBrowserSync'], function (cb) {
    gulp.watch(["./src/**"], function () {
        return runSequence(
      //      'build_PostCss', //PostCSSのビルド
      //     'build_TS',      // TypeScriptのビルド
            'build_Pug',      // Pugのビルド
            'copyExceptionFiles',     // 例外的なファイルのコピー(index.htmlなど)
            'reload'     // browser-syncの強制更新
        );
    });
});

gulp.task('prepareBuild', function (cb) {
  // 省略: watchのrunSequenceと同じ順でタスクを実行(reloadは不要)
});

```

> タスクの実行順が重要です。[run-sequence](https://www.npmjs.com/package/run-sequence)を使用し、実行順を制御してください。

## 実装側

### index.html

headerタグはheadernav.tagを固定参照、mainタグは各パスの情報を見て切り替わります。
具体的には、ルート以下が`/`、`/#default` のとき、`default.tag`を使用、
`/#content`のとき、`content.tag`を使用します。


```html
<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>sample</title>

</head>
<body>
  <header></header>
  <main></main>

<!-- 利用するtagファイルを以下のようにして、取り込みます。 -->
  <script type="riot/tag" src="tags/headernav.tag"></script>
  <script type="riot/tag" src="tags/content.tag"></script>
  <script type="riot/tag" src="tags/default.tag"></script>

  <script src="assets/js/libs/riot+compiler.min.js"></script>
  <script src="assets/js/libs/route.min.js"></script>

  <script>
    //　SPAのルーティングの設定。ヘッダー部
    route(function (tagName) {
      riot.mount('header', 'headernav');
    });
    // SPAのルーティングの設定。メイン部
    route(function (tagName) {
      tagName = tagName || 'default';
      riot.mount('main', tagName);
    });
    route.start(true);
  </script>
</body>
</html>
```

---

### default.pug

defaultページのpugファイルのサンプルです。今回、css、jsの参照先は、default.pugから相対座標で指定してます。

```pug
default
  style
    include ../../tmp/css/default.css
  h1.test
  a(href="#content") gotoContentPage
  .default_container
    button(onclick="{sayHello}") Hello
  script
    include ../../tmp/js/default.js
```

css、jsのinclude先を変えるだけで、過去の負の遺産を封印したり、新しいことを試したときに、実験コードに入れ替えができます。

これだけで、**新しいことを試したい欲求。汚いコードの除去**の両方達成

webpackだと、loaderに引っかかったり、ファイルの退避が必要になってしまうこともあります。

---
### content.pug

defaultページにあるリンクの遷移先のファイルです。
`content.css`と`content.js`は省略します。

```pug
content
  style
    include ../../tmp/css/content.css
  h1.test
  a(href="#") goBackDefaultPage
    include ../../tmp/js/content.js
```

---

### default.css

dafault.pugで使用するcss 

```css
.test{
  color:red;
}
.default_container{
  width:100%;
  padding-top:25px;
}
.default_container>button{
  width:200px;
  height:40px;
}
```

---

### default.js

default.pugで使用するJS

```js
// this. で書くのはriotのお作法。
this.sayHello=function(){
  alert("Hello");
}
```

---

## おわりに

Pugの使い方を見て、webpackで良くないか？ReactやAngular、Vueで良いのでは？と思う人もいると思います。筆者もこれを考え始めた当初、その線で考えていました。
しかし、今回、ターゲットとしたのが、普段Webアプリをやってない人ということで、デバッグさせるとき、webpack使わせるのも酷なのと（ソースマップがあればよいが）、筆者がgulpならまだマシ（人間が把握しきれる）という主観の元、この構成にしました。

別の狙いとして、browser-syncを使ったWebアプリの開発速度に毒したかったというのがあります。
riotについては、今回のパターンに都合がよかったのと、単に使ってみたい欲にかられました。

###### 反省点

* 実はけっこうガチなのを作ってありますが、自部署の製品向けに作りこみ過ぎてしまって、この記事を書くときに、プロジェクトを作り直しました。
* もっとぶっ飛んだ記事を書きたい。JavaScriptで～～をやってみたとか。（それやる意味あった？と突っ込みがあるぐらいの）