読者です 読者をやめる 読者になる 読者になる

俺、サービス売って家買うんだ

正直このペースで作ってればいつか2-3億で売れるのがポっと出来るんじゃないだろうか

Vue.jsのComponentsの使い方

JavaScript Programing Vue.js
f:id:ie-kau:20150818232251p:plain

2015/10/31追記

この記事はVue.js@0.12.1を元に書かれています。最新版は1系統で書式に大きな変更がありますでのご注意ください。 inheritやカスタムタグ周りの書式に変更があります。

Vue.jsを0.12.1から1.0.3にアップデートした際にハマったこと - 俺、サービス売って家買うんだ

ちょっとしたものを作るときにAngularJSを入れるのはだるいなーと思いつつ、jQury+lodashなどのテンプレートを利用してDOMを弄くるのも手がつかれるしめんどうとも思うので、AngularJSと同じようにデータバインディングを備えていてドキュメント量が少ないVue.jsを使う頻度が上がっています。(個人的に)

vuejs.org

コンポーネントについて

実は、Vue.jsはWeb Componentsのようなコンポーネントシステムを利用できます。しかもpolyfillなしで、IE9以上で動かせます。UIが再利用できて便利なのでよく使うのですが、最初のうちはハマってしまった箇所もあったのでメモ書きを。

コンポーネントを作る

準備するHTML

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue.jsのサンプル</title>
</head>
<body>
<div id="app">
  <div v-text="message"></div>
  <my-items items="{{items}}"></my-items>
</div>

<script id="items" type="x-template">
  <p>{{message}}</p>
  <ul>
    <li v-repeat="items">{{$value}}</li>
  </ul>
</script>
</body>
</html>

この時点で何となく想像はつきそうですが#itemsをテンプレートとしたコンポーネントを作成して<my-items>でレンダリングしようという目論見です。

ちなみに、 <my-items items="{{items}}"></my-items>の部分は

<div v-component="my-items" items="{{items}}"></div>

という書き方でも同じです。

通常のやり方

コンポーネントの作成

var MyItems = Vue.extend({
  props: ['items'],
  data: function() {
    return {
      message: 'hello items!'
    }
  },
  template: '#items'
})

VM本体

// コンポーネント登録
Vue.component('my-items', MyItems);
var app = new Vue({
  el: '#app',

  data: {
    message: 'Hello World',
    items: ['りんご', 'いちご', 'バナナ']
  }
});

コンストラクタ経由で登録

var app = new Vue({
  el: '#app',

  components: {
    'my-items': {
      props: ['items', 'message'],
      template: '#items'
    }
  },

  data: {
    message: 'Hello World',
    items: ['りんご', 'いちご', 'バナナ']
  }
});

ポイント

  • props経由で親の$dataを利用する
    • 全ての親の$dataにアクセスしたいときはinherit: trueにする
    • $dataをコンポーネント側で初期化するときは関数で渡す

はまりポイント

  • propsを登録して$dataを渡せるようにすることを忘れない
  • Vueをインスタンス化前に登録する
  • tbodyやtrをコンポーネント化して利用するときはv-component属性を使う
    • table 要素は、要素がその内部に表示できるものに制限があるため、カスタム要素が押し上げられてしまい正しくレンダリングされません。これらのケースではコンポーネントディレクティブシンタックスを使うことができます:

親とやりとりする

親や他のコンポーネントとやりとりする場合はイベントを推奨されているようです。

サンプルHTML

親子にボタンを仕込みます

<div id="app">
  <button v-on="click: onClick">親クリック</button>
  <my-items items="{{items}}"></my-items>
</div>

<script id="items" type="x-template">
  <ul>
    <li v-repeat="items">{{$value}}</li>
  </ul>
  <button v-on="click: onClick">子クリック</button>
</script>

子→親

var MyItems = Vue.extend({
  props: ['items'],
  template: '#items',
  methods: {
    onClick: function() {
      this.$dispatch('child-clicked', this);
    }
  }
})

Vue.component('my-items', MyItems);
var app = new Vue({
  el: '#app',

  created: function() {
    this.$on('child-clicked', function (child) {
      console.log('child is clicked: ')
      console.log(child)
    })
  },

  data: {
    items: ['りんご', 'いちご', 'バナナ']
  }
});

親の$onでイベントをリッスンした上で、子側でクリックが走った際に$dispatchしてやると親でイベントを受け取ることができます。

親→子

var MyItems = Vue.extend({
  props: ['items'],
  created: function() {
    this.$on('parent-clicked', function (parent) {
      console.log('parent is clicked: ')
      console.log(parent)
    })
  },
  template: '#items'
})

Vue.component('my-items', MyItems);
var app = new Vue({
  el: '#app',

  data: {
    items: ['りんご', 'いちご', 'バナナ']
  },

  methods:{
    onClick: function() {
      this.$broadcast('parent-clicked', this);
    }
  }
});

次は逆で子供側でイベントをリッスンした上で、親側のボタンがクリックされた際に$broadcastでvm全体にたいしてイベントを送ってやれば子供側でイベントを受け取ることができます。(この場合は$dispatchではダメ。)

まとめ

すごい量の機能があるわけでもないけど、データバインディングとコンポーネント機能があるだけで、なんとなく痒いところに手がとどくVue.jsは便利です。AngularJSを勉強する暇がないけどJavaScriptのコード量を減らしたいときは是非入れてみるといい気がします。

オラッオラッ

関連書籍