新卒でもモダンなフロント開発がしたい!

  • 80
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

この記事は第2のドワンゴ Advent Calendar 2015の18日目です。

はじめに

こんにちは、2015年新卒入社の@nagisioです。
普段はフロントエンドで開発を行っています。

この記事はjQueryしか書いたことが無かった筆者が、仮想DOMという世界に辿り着くまでの過程を淡々と書いたものです。
過度な期待はしないでください。

また、本記事の内容は私と同じ初心者の方向けとなっています。

フロント開発ってなんなの

まずフロント開発が一体全体何を書くのか抑えておきましょう。
私が普段書いているものは以下です。

  • HTML
  • CSS
  • JavaScript

入社前に私が書いていた(≠書けた)ものも見てみましょう。

  • HTML
  • CSS
  • JavaScript

変わりませんね。
ですが、この大きな枠組みの中に、沢山の技術が含まれているのです。

具体的に述べましょう。

  • HTML
    • Slim
    • Jade
    • ECT
  • CSS
    • Sass(SCSS)
    • Less
    • Stylus
  • JavaScript
    • Gulp/Grunt
    • AltJS/ES6
    • React.js/Riot.js/Mithril.js
    • Mocha/Jasmine

ほんの一部を挙げました。
9割以上知っている方はこの記事はいささか冗長かもしれません。
私は入社時に恐らくSassぐらいしか知らなかった気がします。

本記事ではこの中から

  • Jade
  • Sass(SCSS)
  • Riot.js

を取り上げ、一つの簡単なプロダクト(ToDo的なやつ)を作ってみます。
また、成果物については、下記のgitリポジトリをご覧いただければと思います。

nagisio/riot-todo-test
http://nagisio.github.io/riot-todo-test/

1. HTMLを書く

まずはHTML(の雛形)を書いてみましょう。

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>ToDo</title>
    <link rel="stylesheet" href="./styles/app.css">
  </head>
  <body>
    <section id="todo">
      <form>
        <input type="text" name="title">
        <input type="text" name="description">
        <input type="submit" value="Add">
      </form>
    </section>
    <script src="./scripts/app.js"></script>
  </body>
</html>

簡単ですね。

今回のようなシングルページで完結する場合、ベタにHTMLを書くので良いかもしれません。
ですが、複数ページを作る場合はどうでしょう。
毎回 <head> 内を書くのって面倒じゃないですか?
コピペをすれば簡単?
では <meta> タグが一つ増えたら?
CSSの読み込みファイルが増えたら?
etcetc...

めんどくさくないですか?

モダンの波に乗りたい!

上の例では <head><body> の部分を分けることができれば、幸せになれそうです。
そこでいよいよテンプレートエンジン(Jade)の出番です。
詳しい説明は省きます(後述の素晴らしい参考記事やページを見てください)ので、まずはコードを見てください。

_layout_.jade
doctype html
html(lang='ja')
head
  meta(charset='utf8')
  title ToDo
  link(rel='stylesheet' href='styles/app.css')
body
  block bodyElements

block という見慣れないキーワードがありますが、その他はHTMLのタグ名そのものです。
Jadeではタグの括弧を書かずにインデントで表現します。

さてこのコード、 body タグ内には一見何も書かれていません。

body // bodyタグ
  block bodyElements // 謎のキーワード

Jadeではこの block というキーワードを用いて、継承関係を作ることができます。
bodyElements という名前のブロックを作っておいて、継承先に実装を任せているのですね。

それでは新しいjadeファイル(index.jade)を用意して、実際に bodyElements の中身を書きましょう。
継承には extends というキーワードを使用します。

index.jade
extends _layout_   // _layout_.jadeを継承
block bodyElements // block bodyElementsの中身
  section#todo     // #todoはid="todo"のエイリアス
    form
      input(type='text' name='title')
      input(type='text' name='description')
      input(type='submit' value='Add')
  script(src='./scripts/app.js')

これがいわゆる コンポーネント化 という考え方です。
新しいページを作るときは _layout_.jade を継承すれば、 <head> の中身は気にしなくて良いのです。

この2つのJadeファイルで出来上がったHTMLは、もちろん始めに書いたHTMLファイルそのものです。

参考

2. CSSを書く

HTMLがコンポーネント化できたところで、次はCSSを書いていきましょう。
まずは先ほど書いたHTMLをブラウザで見てみます。

タイトルと説明を入力するテキストボックスと、追加するボタンがあるだけですね。
何だか味気ないので、CSSを使って装飾しましょう。
まずはCSSを書きやすいように、 index.jade の各タグにクラス名を付けておきます。

index.jade
extends layouts/_layout_
block body
  section#todo
    form#form-todo
      p.headline Title
      input(class='form-title'  type='text'   name='title')
      input(class='form-desc'   type='text'   name='description' placeholder='Description')
      input(class='form-submit' type='submit' value='ToDo')
  script(src='./scripts/app.js')

※注
クラスの命名規則に関しては、今回特に考慮していません。
普段はBEMと呼ばれる命名手法に則って書いていますので、興味のある方はそちらもご参照ください。

少し長いですが、以下のように書きました。

app.css
section#todo {
  font-family: sans-serif;
  padding: 10px; 
}

p.headline {
  color: #40C4FF;
  margin: 0;
}

input[type='text'] {
  display: block;
  outline: none;
  border-top: none;
  border-right: none;
  border-left: none;
  border-bottom: 1px solid #EEEEEE;
  padding-bottom: 5px;
  transition: all .3s ease;
  width: 240px;
  color: #424242;
}

input[type='text']:focus {
  border-bottom: 1px solid #40C4FF;
}

.form-title {
  font-size: 2rem;
}

.form-desc {
  margin-top: 2rem;
  font-size: 1rem;
}

.form-submit {
  margin-top: 1rem;
  font-size: 1rem;
  padding: .5rem;
  border: none;
  outline: none;
  color: #424242;
  background-color: #EEEEEE;
  cursor: pointer;
}

もう一度ブラウザで見てみましょう。

いい感じになりましたね。
ただし上記のCSSは危険なCSSです。

style.css
.form-title {
  font-size: 2rem;
}

例えば上記のスタイルは全ての form-title クラスについて適用されてしまいます。
つまり後で違うフォームを作成し、 form-title クラスを付けてしまうと、上記のスタイルが適用されてしまいます(CSSあるあるですね)。

ではスコープを適切に設定すれば良いでしょうか?

app.css
section#form form .form-title {
  font-size: 2rem;
}

これで問題なさそうです。
さてそれでは他のクラスについてもスコープを…

やっぱりめんどくさくないですか?

モダンの波に乗りたい!!

ここではAltCSS(CSSのメタ言語)の一つであるSassを使ってコーディングしてみます。
なお、SassにはSass記法とScss記法と呼ばれる2つの記述方法がありますが、Scss記法の方がもとのCSSの書き方に似ているので、そちらの方が書きやすいと思います。
それではさっそくコードを見てみましょう。

app.scss
$text-color:   #424242;
$accent-color: #40C4FF;
$dividers:     #EEEEEE;

section#todo {

  font-family: sans-serif;
  padding: 10px;

  p.headline {
    color: $accent-color;
    margin: 0;
  }

  input[type='text'] {
    display: block;
    outline: none;
    border-top: none;
    border-right: none;
    border-left: none;
    border-bottom: 1px solid $dividers;
    padding-bottom: 5px;
    transition: all .3s ease;
    width: 240px;
    color: $text-color;

    &:focus {
      border-bottom: 1px solid $accent-color;
    }
  }

  form {
    .form-title {
      font-size: 2rem;
    }

    .form-desc {
      margin-top: 2rem;
      font-size: 1rem;
    }

    .form-submit {
      margin-top: 1rem;
      font-size: 1rem;
      padding: .5rem;
      border: none;
      outline: none;
      color: $text-color;
      background-color: $dividers;
      cursor: pointer;
    }
  }
}

クラスのスタイル定義が入れ子構造になっているのが分かりますね。
そう、Sassでは入れ子構造=スコープに対応しているのです。
つまり上記の .form-title は、CSSに変換すると以下のように展開されます。

app.css
section#form form .form-title {
  font-size: 2rem;
}

また、 app.scss の初めの数行を見てください。

app.scss
$text-color:   #424242;
$accent-color: #40C4FF;
$dividers:     #EEEEEE;

これはいわゆる変数定義です。色を変更したい時は、この行を変更するだけで一括して変えることができるわけですね。

なお、今回はミニマムなページのため、一つの .scss ファイルしか作りませんでした。Sassにももちろんコンポーネント化するための便利な機能が備わっていますので、複数ページを作ったり、ページの情報量が多い場合は、CSSも積極的にコンポーネント化していきましょう。

参考

3. JavaScriptを書く

それではいよいよJavaScriptを書いていきましょう。
まずは先ほど書いた index.jade ファイルに、ToDoリストを追加するための section タグを用意しておきます。

index.jade
extends layouts/_layout_
block body
  section#todo
    form#form-todo
      p.headline Title
      input(class='form-title'  type='text'   name='title')
      input(class='form-desc'   type='text'   name='description' placeholder='Description')
      input(class='form-submit' type='submit' value='ToDo')
  section#list
    // このタグ内にToDoの項目(liタグ)が追加されていく
  script(src='./scripts/app.js')

フォームの submit ボタンを押すと、この section#list タグの中にToDoの項目が追加されるイメージです。
では、どうやって項目を追加すれば良いでしょうか?
もちろん appendChild() といったメソッドを用いて追加することは可能です。
jQuery を使用して、DOM要素を指定して、追加するDOMを用意して、 append() を呼び出して…

めんどくさいですよね!

モダンの波に乗りたい!!!

ついに仮想DOMの出番です。
上記で色々とモダンな(一部モダンではない気もする)波に乗ってきましたが、この仮想DOMは間違いなくモダンな奴です(一年後はどうなっているか分かりません)。

乗るしかない、このビッグウェーブに。

仮想DOMを扱うjsフレームワークはいくつかあり、Reactが有名です。しかしながら、Reactは学習コストが少し高めということもあり、今回は学習コストが低めなRiot.jsを使ってみます。
というのもこのRiot.js、仮想DOMをJadeで書けるのです(!)。

では早速書いてみます。ビッグウェーブに乗りたいのでjsもES6で書きましょう。

※注 ES6については下記の素晴らしい記事をご覧ください…!:pray:

app.js
'use strict';

import Riot from 'riot'
import Todo from './components/todo.jade'

Riot.mount('section#list', Todo);

app.js がやっていることはただひとつ、Todoというコンポーネント(=仮想DOM)を section#list タグ内に追加しているだけです。

Todoコンポーネントを見てみましょう。
TodoコンポーネントはJadeファイルですが、htmlのほかにjs(es6)のコードも含まれています。

components/todo.jade
todo
  ul
    li(each='{ todoList }')
      div.title { title }
      div.description { description }

  script.
    const self = this
    self.todoList = []

    self.on('mount', () => {
      document.getElementById('todo-form')
              .addEventListener('submit', onSubmit, false)
    })

    onSubmit = (e) => {
      e.preventDefault()

      let children    = e.target.children
      let title       = children.title
      let description = children.description

      let todo = {
        title: title.value,
        description: description.value
      }

      self.todoList.push(todo)
      self.update(self.todoList)

      title.value = ''
      description.value = ''
    }

大事なのは

li(each='{ todoList }')

self.todoList.push(todo)
self.update(self.todoList)

です。
このコンポーネントはフォームイベントを受け取り、それぞれの内容(titleとdescription)をオブジェクトとして self.todoList に追加(push)します。
その後 self.update(self.todoList) によって、 self.todoList の内容が変化したことを知らせます。
すると li(each='{ todoList }') が評価され、self.todoList の配列の長さ分だけ li タグが生み出されるのです(それも自動で)!

それでは最終的な成果物を見てみましょう。

http://nagisio.github.io/riot-todo-test/

自動的にTodoが追加されることが確認できました!

参考

おわりに

本記事ではJade/Sass/Riot.jsによる、仮想DOMを用いたモダンなフロント開発について、簡単な例を示しながら紹介しました。
フロントエンドの世界は本記事で紹介しきれない、新しくてわくわくするような技術がまだまだたくさんあります。
まずは小さな世界を覗いて、触れてみて、フロント開発の楽しさを知ってもらえれば幸いです。

私も一フロントエンドエンジニアとして、ますます精進していく所存です。