Vue.js始めるおれおれアドベントカレンダー2016 – 1日目

とにかく動かしてみます。

すぐ触れるやつ

vue.min.js を読み込むやつ用意しておきましたので、自分で試したい場合はこちらでどうぞ。

インストール

公式ドキュメントによると、このCDNを使えとの事だ。

<script src="https://unpkg.com/vue/dist/vue.js"></script>

これだと常に最新版が読み込まれるみたい。更新作業が不要になる半面、互換性のない更新によりアプリが破壊されるかもしれない。(1→2になったタイミングで駄目になったっぽいチュートリアルを最近見かけた。) 個人的には cdnjs の現時点での最新版を使いたいなーと思ってる。

Hello World!

{{nantoka}} みたいな形で文字列として出力するそうです。AngularJSと一緒だ。

<!-- index.html -->
<div id="app">
  <h1>{{message}}</h1>
</div>

JSファイルの読み込み等は省きますので忘れずに追加しておいてください。で、自分のJSの中身。

// helloworld.js
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World!',
  },
});

結果

hello-world
Hello World

やったぜ。

babelだtype scriptだ、とあれこれ環境整えなくてもとりあえず動いてくれるのが嬉しい。

コンポーネント

を早速使ってみたいと思います。やっぱこれ系ライブラリーの真骨頂でしょー!

<div id="app">
  <app-title v-bind:title="message1"></app-title>
  <app-title v-bind:title="message2"></app-title>
</div>

というわけで、HTMLにはないはずの “app-title” という要素を書きました。コンポーネント名=要素名みたいな感じらしい。(ちなみにこれ短縮して <app-title /> にしたら駄目だった。)

Vue.component('app-title', {
  template: '<h1>Vue: {{title}}</h1>',
  props: [
    'title',
  ],
});
  
var app = new Vue({
  el: '#app',
  data: {
    message1: 'Hello',
    message2: 'World!',
  },
});

テンプレート変えたよ。

Vueの文書を見ると先に Vue.component() を使ってコンポーネントを登録するやり方が先に出てくるんだけど、 components: {} の方が範囲が狭まって良い感じ、な気がした。前者はgloballyな登録で、後者はlocallyらしい。

結果

hello-and-world
templateに “Vue:” を追加したの、ちゃんと出てる。

v-bind:title と props

これで親要素(親コンポーネント)から子要素(子コンポーネント)へ値とかを渡すんだって。要素の値 "message" は親要素の data とかから参照されるようだ。逆に文字列を渡したい場合は v-bind:title="'Hello World!'" みたいに、 "..." の中で '...' で括ったらできた。

他にも v-nantoka がいろいろあるみたい。別の日に全部試す。

HTMLをテンプレートへ移動

タグがHTMLファイルとJSファイルとに分かれちゃったので、コンポーネントじゃない部分もJSファイルへ移してみる。

<div id="app"></div>
Vue.component('app-title', {
  template: '<h1>{{title}}</h1>',
  props: [
    'title',
  ],
});
 
var app = new Vue({
  el: '#app',
  template: `
    <button v-on:click="edit_click">編集</button>
    <app-title v-bind:title="message"></app-title>
  `,
  data: {
    message: 'Hello World!',
  },
  methods: {
    edit_click: function(event) {
      var newMessage = window.prompt('タイトル:', this.message);
      this.message = newMessage;
    },
  },
});

できるかなーと思ったけど駄目だった。(´・ω・`)

クリックに反応させる

さてなんかこう見てるだけじゃなくてちょっと触れるようにもしてみます。そんでデータいじる。

<div id="app">
  <button v-on:click="edit_click">編集</button>
  <app-title v-bind:title="message"></app-title>
</div>
Vue.component('app-title', {
  template: '<h1>{{title}}</h1>',
  props: [
    'title',
  ],
});
 
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World!',
  },
  methods: {
    edit_click: function(event) {
      var newMessage = window.prompt('タイトル:', this.message);
      this.message = newMessage;
    },
  },
});

「編集」ボタンを押して message を変更できるようにしました。

結果

edit

this.message = ...

はーこれするだけで画面が更新されるのすごい。すごいよ!

これただのプロパティじゃなくてgetter/setterになってるんだな。this.obj.foo = ... とかも追っかけてくれてる。手厚い。

v-on:click="edit_click"

v-on:click でクリック時の処理を指定する、と。イベントは任意のものでも何でも使えるのかな。

ちなみに

“edit_click” は最近おれの中で流行中の命名規則です。MSVSで書いたC#がこんな感じ、だった気がする。たぶん。ある程度機械的に決まるしわかりやすいし良いかなあと思ってる。

配列を触る

<div id="app">
  <button v-on:click="add_click">追加</button>
  <button v-on:click="change_click">変更</button>
  <ul>
    <li v-for="num in numbers">{{num}}</li>
  </ul>
</div>
var app = new Vue({
  el: '#app',
  data: {
    numbers: [ 1, 2, 3, 4, 5 ],
  },
  methods: {
    add_click: function(event) {
        var newNumber = Math.floor(Math.random() * 100);
        this.numbers.push(newNumber);
    },
    change_click: function(event) {
        var index = Math.floor(Math.random() * this.numbers.length);
        var newNumber = Math.floor(Math.random() * 100);
        this.numbers[index] = newNumber;
    }
  },
});

ランダムに値を追加するのと、ランダムにどこかの値を変更する機能です。

結果

array

動作がおかしい。

v-for で繰り返し

AngularJSと一緒だ。

array.push() で追加

やっぱり簡単じゃんすごい。

array[index] = newValue は駄目

値は変更されるんだけど、画面が更新されない。次回更新時(今回なら「追加」ボタンを押した際)にまとめて更新される。

というわけで、 this.numbers[index] = newNumber の代わりに Vue.set(this.numbers, index, newNumber) としたらうまくいきました。(試してみてね。) はーはー。「JavaScript言語仕様の限界」らしい。まあそうだよな。

コンポーネントとやり取りする

<div id="app">
  <app-form v-bind:message-change2="message_change" v-bind:message2="message"></app-form>
  <app-title v-bind:message1="message"></app-title>
</div>
var appTitle = {
  template: '<h1>{{message1}}</h1>',
  props: [
    'message1',
  ],
};
 
var appForm = {
  template:
    '<div>' +
      '<input v-model="message2" type="text" />' +
      '<button v-on:click="button_click">Update</button>' +
    '</div>',
  props: [
    'message2',
    'messageChange2'
  ],
  methods: {
    button_click: function(event) {
      this.messageChange2(event, this.message2);
    },
  },
};
 
var app = new Vue({
  el: '#app',
  components: {
      appTitle: appTitle,
      appForm: appForm,
  },
  data: {
    message: 'Hello',
  },
  methods: {
    message_change: function(event, newMessage) {
      this.message = newMessage;
    },
  },
});

コンポーネントが二つに本体がひとつで長くなった。あとVue.component の代わりにコンストラクターの components を使ってみました。

値は親が持っておいて、子からはその値を出力する。子の方で変更しても親へは反映されない。イベントを使って更新後の値を親へ通知し、親側でそれを設定すると、子へ反映される。

あ、なんか大変そうだ。あれか、Vuexっていうのを使うと楽なのか。

結果

edit-over-component

ぱかぱかする

気を取り直して簡単なやつ……。よくjQueryでやる show() と hide() 的なやつ。

<div id="app">
  <button v-on:click="toggle_onClick">開閉</button>
  <div v-show="visible">
    <p>やっほー</p>
  </div>
</div>
var app = new Vue({
  el: '#app',
  data: {
    visible: false,
  },
  methods: {
    toggle_onClick: function(event) {
      this.visible = !this.visible;
    },
  },
});

結果

ね、簡単でしょう?

開閉操作は要素ではなく変数で

変数と呼ぶんだろうか、あれ。

this.visible の値が true なら表示、そうでないなら非表示になるのが v-show です。他のやり方でもできそうだけど、やっぱりよく使うから用意してくれたんだろうか。

jQueryだと要素を操作するんだけど、Vueとかでは状態(変数)を操作して、その結果に応じてVueが要素を更新するという二段階になってます。複雑っぽいけど、でもその分見た目と状態が分割できてかえって簡潔になる。

これを回りくどいと感じるか、綺麗でわかりやすいと感じるか。jQueryどっぷりの人にとっては新しい世界観が必要になるかもね。

アニメーションは

どうするんだろ? jQueryの slideToggle() みたいなの。

どうやら何やら transition という仕組みがあるみたい。あるいは、クラスの付け替えとCSSの transition プロパティで良いかな。

アニメーション

<div id="app">
  <button v-on:click="toggle_onClick">開閉</button>
  <transition>
    <div v-show="visible" class="box">
      <p>やっほー</p>
    </div>
  </transition>
</div>
body { margin: 3em; }
.box {
  background-color: black;
  border: 1px solid #9cf;
  transition: all 3s;
}
.v-enter {
  opacity: 0;
  background-color: blue;
}
.v-enter-active {
  opacity: 1;
  background-color: red;
}
.v-leave {
  opacity: 1;
  background-color: green;
}
.v-leave-active {
  opacity: 0;
  background-color: yellow;
}
var app = new Vue({
  el: '#app',
  data: {
    visible: false,
  },
  methods: {
    toggle_onClick: function(event) {
      this.visible = !this.visible;
    },
  },
});

<transition> で括った内側で v-show とか使うと、クラスを付け外ししてくれるんだって。それでそれに合わせてCSS書けばアニメーションするんだって。

結果

transition
各段階で背景色を変えました。

クラス

この四つ。

  • v-enter
  • v-enter-active
  • v-leave
  • v-leave-active

わからん

出てくるときは v-enter から v-enter-active へ、消えるときは v-leave から v-leave-active へ、みたいな感じだと思うんだけど、よくわかんない。

動くには動いたけど、まだ流れがつかめてない。

という感じで

今日はここまで。

導入が簡単なのでReactとか、AngularJSないし同2より人に薦めやすい気がする。Riot.jsも面白そうだけど。自分がまだ知らないので「良いぞ」じゃなくて「良さそうだぞ」くらいだけどね。

あとVueに限った話じゃないけど、自分だけでやってると何が正しいのかよくわからんすね……。誰かに教えてもらいたいわ。