aurelia のチュートリアルを実装しつつ他のフレームワークと比較してみた

# motivation
*   `aurelia` のチュートリアルを実装してみる
*   同じものを他のフレームワークで実装してみる
*   なんとなくそれぞれの雰囲気を感じ取り、比較した気分に浸る

\* 雰囲気を掴むだけなので深くはみない。実装も割と雑。

# frameworks
*   [aurelia](http://aurelia.io)
*   [Vue.js](https://jp.vuejs.org/index.html)
*   [React](https://facebook.github.io/react/)

\* 他のフレームワークも試してみたら適宜追加していく。

# todo app
なんでこの手のチュートリアルはTODOアプリなんだろうと思いつつ。  
(フォーム・リスト・アイテムとコンポーネントの分割、各種アクションと基本的なものが学べるイイ教材？)

## todo class

```javascript:todo.js
export default class Todo {
  constructor(description) {
    this.description = description;
    this.done = false;
  }
}
```

タスクの概要と、進捗を要素として持つ。

## case aurelia

```html:app.html
<template>
  <h1>${heading}</h1>

  <form submit.trigger="addTodo()">
    <input type="text" value.bind="todoDescription">
    <button type="submit">Add Todo</button>
  </form>

  <ul>
    <li repeat.for="todo of todos">
      <input type="checkbox" checked.bind="todo.done">
      <span css="text-decoration: ${todo.done ? 'line-through' : 'none'}">
        ${todo.description}
      </span>
      <button click.trigger="removeTodo(todo)">Remove</button>
    </li>
  </ul>
</template>
```

```js:app.js
import Todo from './Todo';

export class App {
  constructor() {
    this.heading = 'Todos';
    this.todos = [];
    this.todoDescription = '';
  }

  addTodo() {
    if (this.todoDescription) {
      this.todos.push(new Todo(this.todoDescription));
      this.todoDescription = '';
    }
  }

  removeTodo(todo) {
    let index = this.todos.indexOf(todo);
    if (index !== -1) {
      this.todos.splice(index, 1);
    }
  }
}
```

少ないコードでスッキリ書ける。

## case vue

```html:App.vue
<template>
  <div>
    <h1>{{heading}}</h1>

    <form v-on:submit.prevent="addTodo()">
      <input type="text" v-model="todoDescription">
      <button type="submit">Add Todo</button>
    </form>

    <ul>
      <li v-for="todo of todos">
        <input type="checkbox" v-model="todo.done">
        <span v-bind:class="[todo.done ? 'done' : '']">
          {{todo.description}}
        </span>
        <button v-on:click="removeTodo(todo)">Remove</button>
      </li>
    </ul>
  </div>
</template>

<script>
import Todo from './todo';

export default {
  name: 'App',
  data() {
    return {
      heading: 'Todos',
      todoDescription: '',
      todos: [],
    };
  },
  methods: {
    addTodo() {
      if (this.todoDescription) {
        this.todos.push(new Todo(this.todoDescription));
        this.todoDescription = '';
      }
    },
    removeTodo(todo) {
      const index = this.todos.indexOf(todo);
      if (index !== -1) {
        this.todos.splice(index, 1);
      }
    },
  },
};
</script>

<style>
  .done {
    text-decoration: line-through;
  }
</style>
```

Vue.js のシンタックスはちょっと苦手。

## case react

```js:App.js
import React, { Component } from 'react';
import update from 'immutability-helper';
import Todo from './todo';

class App extends Component {
  constructor() {
    super();
    this.state = {
      heading: 'Todos',
      todos: [],
      todoDescription: ''
    }
  }

  onInput(e) {
    this.setState({ todoDescription: e.currentTarget.value });
  }

  addTodo(e) {
    e.preventDefault();
    if (this.state.todoDescription) {
      this.setState({
        todos: update(this.state.todos, {$push: [new Todo(this.state.todoDescription)]}),
        todoDescription: ''
      });
    }
  }

  removeTodo(todo) {
    const index = this.state.todos.indexOf(todo);
    if (index !== -1) {
      const prevTodos = this.state.todos;
      const nextTodos = update(prevTodos, {$splice: [[index, 1]]});
      this.setState({
        todos: nextTodos
      });
    }
  }

  render() {
    const todos = this.state.todos.map((todo, i) => (
      <li>
        <input type="checkbox" checked={todo.done}
          onChange={
            () => {
              this.setState({
                todos: update(this.state.todos, {[i]: {done: {$set: !todo.done}}})
              });
            }
          }/>
        <span style={todo.done ? { textDecoration: 'line-through' } : {} }>
          {todo.description}
        </span>
        <button onClick={() => this.removeTodo(todo)}>Remove</button>
      </li>
    ));

    return (
      <div>
        <h1>{this.state.heading}</h1>
        <form onSubmit={e => this.addTodo(e)}>
          <input type="text" onChange={e => this.onInput(e)} value={this.state.todoDescription}/>
          <button type="submit">Add Todo</button>
        </form>
        <ul>
          {todos}
        </ul>
      </div>
    );
  }
}

export default App;
```

コード量は多いけど、スクリプトだけで書けるのは良い。

# summary
見出しに英語を使うと、ちょっと出来てる感が醸し出せる。