329
309

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

体で覚えるVue.js - ViewModel生成編 〜 JSおくのほそ道 #022

Last updated at Posted at 2014-09-17

こんにちは、ほそ道です。

今回はMV**なフレームワークのひとつであるVue.jsを取り上げてみます。
さて、どこから手をつけたら良いものかと思案にくれた結果、序論や諸注意点をアレコレ展開する前にまずはビシバシと弄って、ヒジョーにシンプルなサンプルをいっぱい作って体にしみ込ませてみるのがイイんじゃないかと思いました。
ボリュームがあるので何回かに分け、今回はビューモデルを生成するパターンをまとめます。

「考えるな、感じろ」の精神でやった後にどう考え学習すべきかや、どう設計すべきかなどの私見は述べられたら良いなと思っております。

ViewModel生成編
ディレクティブ編
[インスタンスメンバ編]
(http://qiita.com/setzz/items/ebbfcc3565bcd27f344c)
[グローバルメソッド編]
(http://qiita.com/setzz/items/8f06f2fe6049173b17f9)
[フィルター編]
(http://qiita.com/setzz/items/e37c47d3f22e5738eb84)
[v-repeatネスト編]
(http://qiita.com/setzz/items/6f22ebd15bcc0afe1ab0)
全体の目次はこちら

ザックリとしたVue.js概念

  • IE8以下には非対応
  • MVVMアーキテクチャにフォーカスしている
  • MVVMとはモデル(M)とビュー(V)間のやり取りをビューモデル(VM)によって行うアーキテクチャらしい

Vueインスタンスの生成

Vueインスタンス

ビューモデルというのを作ってDOM要素にバインドするのが基本的な流れのようです。
サンプルコードはHTMLのbody要素におブチ込み頂けると動くかと思います。
ちょいちょいv-というディレクティブが入ってきますが次回やるので今回はさらっと流します。

el,dataオプション

まずは基本のサンプルとしてel,dataオプションを使ってみます。
データバインディングを体で感じでみます。

オプション 説明
el セレクタを使ってバインド先のDOM要素を指定する
data ビューモデルが保持するデータ群。
データバインディングして画面に表示してみる
<div id="sample">
  <div v-text="bind1"></div>
  <div v-model="bind2"></div>
  <div>{{bind3}}</div>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      bind1: '111',
      bind2: '222',
      bind3: '333'
    }
  });
</script>
  • new Vue()でビューモデルを作成する
  • elでDOM要素を指定し、ビューモデルの適用範囲を指定する
  • ビューモデルのdataオブジェクトの属性はv-textやv-modelディレクティブによってバインドされる
  • vm.$elvm.$dataでビューモデルの属性にアクセス出来る
  • vm.$datavm.$methodsvmでアクセスも可能
  • ブラウザコンソールからvm.bind1 = 100;と叩けば画面の表示も遷移無しに変更される

今回、dataの中にはただのデータを突っ込んでいますが、モデルはこの中に定義するのもやり方としてあるようです。
また、疑問に思ったのでやってみたのですが1つのDOM要素に複数のビューモデルがバインドされた際には先にバインドしたビューモデルが勝つようです。

とりあえずビュー側(div要素)にv-textやv-modelディレクティブというのが出てきますが今回の対象外なので、あーなんかうまい事データバインドするのね、くらいの理解にとどめます。

methods,computedオプション

固定の値だけではなく、動的な値をデータバインドするときにはこんなオプションが使えるようです。

オプション 説明
methods ビューモデルのメソッドを保持するオブジェクト。methodsに限らないがビューモデル内で定義されたメソッド内でのthisはビューモデル自身を指す
computed 動的なプロパティを生成するメソッドを持つオブジェクト。methodsとの違いとして呼び出しに括弧を用いずプロパティであるかのようにアクセス出来る。またgetやsetの定義が出来る
  • methodsはビューからは括弧付きで呼び出される
methodsを使ったデータアクセス
<div id="sample">
  <div v-text="fullName()"></div>
</div>
<script src="js/vue.js"></script>
<script>
  var user = new Vue({
    el: '#sample',
    data: {
      firstName: 'Taro',
      lastName: 'Yamada'
    },
    methods: {
      fullName: function() {
          return this.firstName + ' ' + this.lastName;
      }
    }
  });
</script>
  • computedは括弧無しであたかもプロパティであるかのようにアクセス出来る
computedを使ったデータアクセス
<div id="sample">
  <div v-text="fullName"></div>
</div>
<script src="js/vue.js"></script>
<script>
  var user = new Vue({
    el: '#sample',
    data: {
      firstName: 'Taro',
      lastName: 'Yamada'
    },
    computed: {
      fullName: {
        get: function() {
          return this.firstName + ' ' + this.lastName;
        }
      }
    }
  });
</script>

methodsとcomputedの使い分け

最終的には、2つのアプローチは完全に同じ結果になります。
しかし、computedプロパティは依存関係にもとづきキャッシュされるという違いがあります。computedプロパティは、依存するものが更新されたときにだけ再評価されます。つまり、dataで定義された中身が変わらない限り、computedプロパティで定義された関数に何度アクセスしても、再び実行することなく以前計算された結果を即時に返します。

Date.now() はリアクティブな依存ではないため、次の算出プロパティは二度と更新されません:

キャッシュされる例
computed: {
  now: function () {
    return Date.now()
  }
}

対称的に、メソッド呼び出しは、再描画が起きると常に関数を実行します。

paramAttributesオプション

el要素の属性を取得できます。

オプション 説明
paramAttributes 配列形式でHTML要素の属性名を列挙するとビューモデルのプロパティとして格納される
  • 属性の取得
el要素のstyle属性にアクセス
<div id="sample" style="font-size:30px">foo</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    paramAttributes: ['style']
  });
  console.log(vm.style);  // font-size:30px
</script>

template, replaceオプション

テンプレートを使う事でHTMLの内容を書き換えます

オプション 説明
template $elで指定した要素に挿入されるHTML文字列
replace templateとセットで使う事で$el要素自体を置換するかをbool値で指定する
  • el要素の内側をテンプレートで置換(上記のサンプルでは元々のdiv要素は存在し続け、内側のfooが置換されます。)
テンプレート
<div id="sample">foo</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    template: '<div>bar</div>'
  });
  console.log(vm.style);  // font-size:30px
</script>
  • replaceを使うと置換される事でel要素が破棄される
テンプレートでリプレース
<div id="sample">foo</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    template: '<div>bar</div>',
    replace: true
  });
</script>

tagName, id, className, attributesオプション

ここまではel要素はあらかじめ存在するものとして扱ってきましたが、生成し挿入する事も出来ます。
インスタンス生成時のelオプションを省略するとデフォルトでは空のdiv要素がel要素として生成されます。下記はそのカスタマイズオプションです。

オプション 説明
tagName elを省略したときに自動生成される要素をdiv以外で指定出来る
id $el要素のid属性で文字列で指定する
className $el要素のclass属性で文字列で指定する
attributes $el要素の属性でオブジェクトリテラルで指定する
  • elの指定を省略すると自動でdivタグが生成され、これはDOM要素としてJSで扱う事が出来る。この時にid,className,attributesで属性の指定が出来る。
elなDOM要素の生成・追加
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    tagName: 'p',
    id: 'foo',
    className: 'bar',
    attributes: {
      style: 'font-size:30px'
    },
    template: 'display text'
  });
  document.body.appendChild(vm.$el);
</script>
生成されるHTML
<p id="foo" class="bar" style="font-size:30px">display text</p>

今回、あえてDOMメソッドのAppendChildを使ってますが、DOM要素の追加はvm$.appendTo(追加先DOM要素);などでやるのがVue.jsのお作法なようです。

イベントフックな関数定義オプション

ビューモデルに特定の状態が発生したときに実行されるコールバック関数の定義オプションです。
今回はcreatedとattachedを取り上げてみます。

オプション 説明
created ビューモデルの初期化時、データバインド前に実行される関数。$dataに動的なプロパティを追加出来る。関数内でthis.$watchメソッドを呼び出すとメソッド引数として渡したコールバック関数がデータバインド時に実行される
ready データバインドが完了した状態で実行される関数。$dataへのプロパティ追加は無視される
attached $el要素がDOMツリーに追加されたときに呼ばれる関数
detatched $el要素がDOMツリーから削除されたときに呼ばれる関数
beforeDestroy vm.$destroy()関数が呼ばれビューモデルが破棄される前に呼ばれる関数
afterDestroy vm.$destroy()関数が呼ばれビューモデルが破棄された後に呼ばれる関数

createdオプション

  • オブジェクト定義時に$dataにデータを定義。
初期化時コールバック
<div id="sample" v-text="value"></div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    created: function() {
      this.$data.value = 'Hello!';
    }
  });
</script>
  • $watchを使ってデータバインド時のコールバックを行う
  • ページ表示後にブラウザコンソールでvm.value = 'Bye!';などと叩けば再度コールバックは実行される
バインド時コールバック
<div id="sample" v-text="value"></div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      value: 'Hello!'
    },
    created: function() {
      this.$watch('value', function(newValue) {
        console.log('reset data: ' + newValue);
      });
    }
  });
</script>

attachedオプション

  • ページ表示後にブラウザコンソールでvm.$appendTo(document.body);を叩くとアラートが表示される。DOMメソッドのappendChildや$el要素のアクセスでは関数は実行されない。
el追加時コールバック
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    attached: function() {
      alert('ViewModel is attached!');
    }
  });
</script>

カスタムパーツ

オプション 説明
directives カスタムディレクティブを提供する
filters カスタムフィルタを提供する
components 部品化したDOM要素を提供する
partials 既存のDOM要素を部品化して再利用出来る
transitions CSSトランジション(使い方がわからなかった。。)

directiveオプション

カスタムディレクティブv-consolelogの実装(コンソールにバインドした値を表示)
<div id="sample" v-consolelog="foo">Hello!</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'CustomDirective',
    },
    directives: {
      consolelog: function(value) {
        console.log(value);
      }
    }
  });
</script>

filtersオプション

配列をソートするカスタムフィルタmysortを実装
<div id="sample">
      <span v-text="foo | mysort"></span>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: [4, 2, 3, 1, 5]
    },
    filters: {
      mysort: function(value) {
        return value.sort()
      }
    }
  });
</script>

componentsオプション

componentはelで指定した要素の内側には使用できるがel要素自体に使おうとすると警告が発生する

DOM要素の内側をコンポーネントで差し替え
<div id="sample">
  <span v-component="foo"></span>
</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    components: {
      foo: {
        template: '<p>Component</p>'
      }
    }
  });
</script>

partialsオプション

  • partialsにアクセスするにはv-partial以外にも{{> property}}という記述も可能
既存のDOM要素を部品化して再利用
<div id="sample">
  <span v-partial="foo"></span>
  <span>{{> foo}}</span>
</div>
<div id="other">OTHER</div>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    partials: {
      foo: '#other'
    }
  });
</script>

parentオプション

オプション 説明
parent ビューモデルに親子関係を持たせる。子は親のデータにアクセス可能
子から親のデータを引っ張ってバインド
<div id="sample">
  <span v-text="foo"></span>
  <span v-text="bar"></span>
</div>
<script src="js/vue.js"></script>
<script>
  var parent= new Vue({
    data: {
      bar: 'BAR'
    }
  });
  var child = new Vue({
    el: '#sample',
    parent: parent,
    data: {
      foo: 'FOO'
    }
  });
</script>

lazyオプション

オプション 説明
lazy INPUT編集時などに、同期的に値が書き換えられず、フォーカスを外れてからデータが更新される
フォーカスを外れたら更新(lazyをfalseにもしてみると違いが一目瞭然)
<form id="sample" action="">
  <div v-text="foo"></div>
  <input type="text" v-model="foo">
</form>
<script src="js/vue.js"></script>
<script>
  var vm = new Vue({
    el: '#sample',
    data: {
      foo: 'defaultValue',
    },
    lazy: true
  });
</script>

===

今回は以上です。
結構手探り間がありますが、体で覚え中なので致し方ないかなと、とにかく前に進むのみ!
次回はディレクティブを体で覚えていきたいと思います。

329
309
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
329
309

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?