end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

Vue.js の練習

https://jp.vuejs.org/

先程のエントリでは Reactでしたが、Vue.jsも触れたことがない為、dotinstall.com にて写経。

ReactはJSXで実装しますが、Vue.jsはjavascriptで実装できる為、 こちらの方が入門しやすい印象です。

以下は、写経した 2つのサンプルで、詳細はsrcを読めば分かります。

写経 その1 - TODO管理

html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue.jsの練習</title>
  <link rel="stylesheet" href="css/styles.css">
</head>
<body>

  <div id="app" class="container">
    <h1>
      <button @click="purge">完了TODOを一括削除</button>
      TODO一覧
      <span class="info">({{ remaining.length }}/{{ todos.length }})</span>
    </h1>
    <ul>
      <li v-for="(todo, index) in todos">
        <input type="checkbox" v-model="todo.isDone">
        <span :class="{done: todo.isDone}">{{ todo.title }}</span>
        <span @click="deleteItem(index)" class="command">[x]</span>
      </li>
      <li v-show="!todos.length">残っているTODOはありません!</li>
    </ul>
    <form @submit.prevent="addItem">
      <input type="text" v-model="newItem">
      <input type="submit" value="追加">
    </form>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script src="js/main.js"></script>
</body>
</html>

javascript

(function() {
    'use strict';
    
    var vm = new Vue({
    el: '#app', // vueを実体化するelementの指定
    data: {
        newItem: '',
        todos: []
    },
    computed: {
        remaining: function() {
        return this.todos.filter(function(todo) {
            return !todo.isDone;
        });
        }
    },
    watch: {
        todos: { // todoに変更があった場合、localStorageに保存
        handler: function() {
            localStorage.setItem('todos', JSON.stringify(this.todos));
        },
        deep: true // hash内部をdeepにwatch
        }
    },
    // 起動直後に localStorage から load
    mounted: function() {
        this.todos = JSON.parse(localStorage.getItem('todos')) || [];
    },
    methods: {
        addItem: function() {
        var item = {
            title: this.newItem,
            isDone: false
        };
        this.todos.push(item);
        this.newItem = '';
        },
        deleteItem: function(index) {
        if (confirm('削除してよろしいですか?')) {
            this.todos.splice(index, 1);
        }
        },
        purge: function() {
        if (!confirm('削除してよろしいですか?')) {
            return;
        }
        this.todos = this.remaining;
        }
    }
    });
})();

css

body {}

.container {
  width: 400px;
  margin: auto;
}

#app h1 {
  font-size: 20px;
  border-bottom: 1px solid #888;
}

#app li {}

#app input[type="text"] {}

.command {
  font-size: 12px;
  cursor: pointer;
  color: #08c;
}

#app ul {
  padding: 0;
  list-style: none;
}

#app li > span.done {
  text-decoration: line-through;
}

.info {
  color: #888;
  font-weight: normal;
}

#app h1 > button {
  float: right;
}

写経 その2 - ボタンクリック カウンタ

html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue.jsの練習 2</title>
  <link rel="stylesheet" href="css/styles.css">
</head>
<body>

  <div id="app">
    <p>総合計: {{ total }}</p>
    <like-component message="Like"    @increment="incrementTotal"></like-component>
    <like-component message="Awesome" @increment="incrementTotal"></like-component>
    <like-component message="Great"   @increment="incrementTotal"></like-component>
    <like-component @increment="incrementTotal"></like-component>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script src="js/main.js"></script>
</body>
</html>

javascript

(function() {
    'use strict';

    var likeComponent = Vue.extend({
    props: {
        message: {
        type: String,
        default: 'Like'
        }
    },
    data: function() {
        return {
        count: 0
        }
    },
    template: '<button @click="countUp">{{ message }} {{ count }}</button>',
    methods: {
        countUp: function() {
        this.count++;
        this.$emit('increment'); // eventの発行
        }
    }
    });
    
    var app = new Vue({
    el: '#app',
    components: {
        'like-component': likeComponent
    },
    data: {
        total: 0
    },
    methods: {
        incrementTotal: function() {
        this.total++;
        }
    }
    });
})();

css

body {}