12周年記念パーティ開催! 2024/5/10(金) 19:00

ライトに使うVue.js 第1回 コンポーネントの基礎

Webアプリケーションのプログラミングは担当しないけれど、Webサイトでもう少し複雑な仕組みを提供したい人向けに、Vue.jsの使い方を解説します。まずは、基本となるコンポーネント作成を解説します。

発行

著者 小山田 晃浩 フロントエンド・エンジニア
ライトに使うVue.js シリーズの記事一覧

はじめに

CodeGridでは、過去にもVue.jsの解説を行っています。以前連載したシリーズでは、.vueファイルを使ったWebアプリケーション開発が前提の内容が中心でした。

その連載でも触れられていましたが、Vue.jsはその柔軟さから、気軽に使えるフレームワークでもあります。jQueryと同じように<script>タグでvue.jsを読み込むだけでも動作しますし、その場合、ビルドもいりません。

この記事では、これからVue.jsを使う人や、Webアプリケーションのプログラミングは担当しないけれど、Webサイトでもう少し複雑な仕組みを提供したい人向けに、気軽に導入できるVue.jsの使い方をご紹介します。

なお、紹介するサンプルは次のリポジトリからダウンロード、またはクローンできます。併せて参照してください。

ライトに使うVue.js

Vue.jsの特徴

Vue.jsは、ReactやAngularともよく比較される、コンポーネント構築のためのフレームワークです。Vue.jsでできることはReactと非常によく似ています。

その一方で、Vue.jsはReactやAngularと違い、ちょっとしたクライアントワークにも気軽に使える存在です。というのも、Vue.jsにはビルドをしなくても使えるという特徴があるためです。

最近のWebページでは、絞り込みUIなどといった、複雑な仕組みが求められることもあります。Webページでできる「普通」のレベルが上がってきています。Vue.jsのようなフレームワークなら、データと表示を分けて管理することで、複雑な仕組みもうまくコントロールできます。

また、仕組みが複雑になると、インラインSVGが混ざったり、レイアウトが複雑な入れ子になったりと、HTMLがとても複雑に長くなってしまう、ということもあります。しかし、Vue.jsのコンポーネントの仕組みを利用すれば、長く複雑な入れ子のHTMLをもっとスッキリした見やすい独自タグで管理することができます。

Vue.jsはJSONなどで提供されたデータを表やリストに整形するのも得意です。

こうした特徴を活かせば、スパゲティーのように絡み合うソースコードになりがちなコードも、茹でる前の乾麺のように整理しやすくなるでしょう。

ハロー、Vue.js

さっそくVue.jsを使ってみましょう。jQueryを扱えるレベルの人なら、ここで解説する内容の理解には困らないでしょう。

1. vue.jsを手に入れる

まずは、vue.jsを手に入れましょう。

Vue.jsの開発サイトから、ZIPファイルでソースコードをダウンロードし、distフォルダの中から、vue.jsを取り出して利用しましょう。そのほかのファイルは不要です。

あるいは、気軽に試すだけなら、CDNからhttps://cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js<script>タグでそのまま読み込んでもいいでしょう。

ここで得られたvue.jsをHTMLに読み込みます。

vue.jsファイルの読み込み

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>

<script src="vue.js"></script>

</body>
</html>

CDNから取得しているなら、

CDNからの読み込み

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>

</body>
</html>

とします。

2. コンテナーを用意する

準備として、Vue.jsで構築する領域をHTML上に用意しておきます。

ここでは、MyAppRootというid名のdiv要素を用意していますが、名前は何でもかまいません。また、id属性ではなくclass属性や、カスタムdata属性でも問題ありません。

Vue.jsで構築する領域の準備

...省略...
<body>

<div id="MyAppRoot"></div>

<script src="vue.js"></script>

</body>
</html>

3. ルートコンポーネントを用意する

さて、準備はできました。ここから、Vue.jsの世界に入っていきましょう!

ルートとなるコンポーネントを用意します。ルートコンポーネントは、new Vue( ... )の形式で実体(インスタンス)を生成します。newを付けることを忘れないでください。

内容は、template部分に、HTMLを書くだけです。そして、これを「2. コンテナーを用意する」の工程で準備した要素にマウントすれば、表示することができます。

要素にマウントする

...省略...
<body>

<div id="MyAppRoot"></div>

<script src="vue.js"></script>
<script>
var componentRoot = new Vue( {

  template: '<div>hello!</div>',

} );

// 要素にマウントする
componentRoot.$mount( '#MyAppRoot' );
</script>

</body>
</html>

hello!の文字が表示されましたか? ようこそ、Vue.jsの世界へ。

次節以降、今書いてもらったコードのポイントや注意点を解説します。

templateの注意点

Vue.jsのtemplateは必ず1つのHTML要素の中に、内容を展開するという決まりがあります。たとえば、次の記述はVue.jsのテンプレートとしては不正です。

不正:2つの要素に展開している

template: '<div>hello!</div><div>Vue.js!</div>',

不正:HTML要素がない

template: 'hello! Vue.js!',

正しくは次のようにします。

正:1つのHTML要素に展開する

template: '<h1>hello! Vue.js!<h1>',

また、次のように1つのHTML要素の中に、子要素として複数のHTML要素が展開されるのは、問題ありません。

正:1つのHTML要素の中に複数のHTML要素を展開している

template: '<div><div>hello!</div><span>Vue.js!</span></div>',

もちろんHTML要素の中が空でも問題ありません。

正:空のHTML要素が1つある

template: '<div id="MyAppRoot"></div>',

子コンポーネントクラスを作る

ルートコンポーネントの中には、任意の子コンポーネントを展開することができます。

ルートコンポーネントは、newを使い実体(インスタンス)を作る必要がありました。一方で、子コンポーネントは「実体を作成するのではなく、クラスのみを用意する」という違いがあります。

ここでは、アイコンをコンポーネントクラスにしてみましょう。

アイコンはSVGで用意しました。これをStarIconという名前のクラスにします。クラスを用意するには、Vue.extend関数を利用します。実体を作成しないのでnewは不要です。

SVGアイコンの子コンポーネントクラス

...省略...
<body>

<div id="MyAppRoot"></div>

<script src="vue.js"></script>
<script>
var StarIcon = Vue.extend( {
  template:
  '<svg width="20" height="20" viewBox="0 0 16 16">' +
    '<path fill="#FBC02D" d="M16 6.2l-5.3-1.12-2.7-4.69v13.01l4.94 2.21-.57-5.39 3.63-4.02zm0 0"/>' +
    '<path fill="#FDD835" d="M5.3 5.09l-5.3 1.11 3.63 4.02-.57 5.39 4.94-2.21v-13.01l-2.7 4.7zm0 0"/>' +
  '</svg>'
} );
</script>

</body>
</html>

次に、このコンポーネントを、「ルートコンポーネントを用意する」の工程で用意したルートコンポーネント内に展開してみましょう。

componentRootの中には、components部がありますので、ここに子コンポーネントクラスを明示します。そして、template部で、クラス名と同じ名前の独自タグを使うだけです。

template部では、クラスの名称での大文字区切り(StarIcon)を、独自タグではハイフン区切り(<star-icon></star-icon>)にする必要がある点に注意してください。

ルートコンポーネントに展開する

...省略...
<body>

<div id="MyAppRoot"></div>

<script src="vue.js"></script>
<script>
var StarIcon = Vue.extend( {
  template:
  '<svg width="20" height="20" viewBox="0 0 16 16">' +
    '<path fill="#FBC02D" d="M16 6.2l-5.3-1.12-2.7-4.69v13.01l4.94 2.21-.57-5.39 3.63-4.02zm0 0"/>' +
    '<path fill="#FDD835" d="M5.3 5.09l-5.3 1.11 3.63 4.02-.57 5.39 4.94-2.21v-13.01l-2.7 4.7zm0 0"/>' +
  '</svg>'
} );

var componentRoot = new Vue( {

  template:
  '<div>' +
    '<star-icon></star-icon>' +
    'hello!' +
  '</div>',

  components: {
    StarIcon: StarIcon
  }

} );

componentRoot.$mount( '#MyAppRoot' );


</script>

</body>
</html>

これで、独自のタグを定義することができました。子コンポーネントクラスは、一度定義してしまえば使い回すのは簡単です。template部に、<star-icon></star-icon>を追加するだけです。

コンポーネントを使い回す

...省略...
var componentRoot = new Vue( {

  template:
  '<div>' +
    '<star-icon></star-icon>' +
    'hello!' +
    '<star-icon></star-icon>' +
    '<star-icon></star-icon>' +
  '</div>',

  components: {
    StarIcon: StarIcon
  }

} );

属性の受け渡し

子コンポーネントに対し、属性を受け渡せるようにし、使い回しの柔軟性を上げてみましょう。

まず、子コンポーネント側で属性を受け取る準備をします。属性を受け取るためには、propsという領域を定義します。今回は、size属性を独自に作り、アイコンの大きさを変えられる仕組みを用意します。

sizeを外から属性として受け付け、その既定値は20である」という定義を行います。その上で、svgのwidth属性とheight属性の値が、独自size属性から流し込まれるようにします。これをバインドと言います。

バインドするためには、元の属性名の最初に「:」を付けます。次のコードではsvgの属性が:width="size"、および:height="size"のように:付きの属性となっていることに注目してください。

属性を受け渡す

...省略...
var StarIcon = Vue.extend( {

  props: {
    size: {
      type: Number,
      default: 20
    }
  },

  template:
  '<svg :width="size" :height="size" viewBox="0 0 16 16">' +
    '<path fill="#FBC02D" d="M16 6.2l-5.3-1.12-2.7-4.69v13.01l4.94 2.21-.57-5.39 3.63-4.02zm0 0"/>' +
    '<path fill="#FDD835" d="M5.3 5.09l-5.3 1.11 3.63 4.02-.57 5.39 4.94-2.21v-13.01l-2.7 4.7zm0 0"/>' +
  '</svg>'
} );

この時点では、親要素から属性値が流し込まれていないため、既定値である20がsize属性値に展開されることになります。

親から属性値を受け渡してみましょう。すでに子コンポーネント側で独自size属性を定義していますので、親からはHTMLと同じ感覚で属性値を明示するだけです。ここでは、20(既定値)、4060をそれぞれ設定しています。ここでもバインド記法を利用しています。

size属性値を渡す

var StarIcon = Vue.extend( {

  props: {
    size: {
      type: Number,
      default: 20
    }
  },

  template:
  '<svg :width="size" :height="size" viewBox="0 0 16 16">' +
    '<path fill="#FBC02D" d="M16 6.2l-5.3-1.12-2.7-4.69v13.01l4.94 2.21-.57-5.39 3.63-4.02zm0 0"/>' +
    '<path fill="#FDD835" d="M5.3 5.09l-5.3 1.11 3.63 4.02-.57 5.39 4.94-2.21v-13.01l-2.7 4.7zm0 0"/>' +
  '</svg>'
} );

var componentRoot = new Vue( {

  template:
  '<div>' +
    '<star-icon></star-icon>' +
    '<star-icon :size="40"></star-icon>' +
    '<star-icon :size="60"></star-icon>' +
    'hello!' +
  '</div>',

  components: {
    StarIcon: StarIcon
  }

} );

componentRoot.$mount( '#MyAppRoot' );

どうです? 一度コンポーネントを作ってしまえば使い回しは簡単でしょう?

属性の表示

独自属性は、そのまま属性値として利用することもできますし、要素内容として表示することもできます。ここでは、AbsoluteNumberという名前で子コンポーネントクラスを用意し、数字を表示する例を見てみましょう。

内容を表示するためには、{{}}で括る記法を利用します。この括弧の中ではJavaScriptの式として評価されますので、単に値を表示する以外にも、ある程度、値をJavaScriptで操作することも可能です。

ここでは、valueという入力値がMath.absで絶対値に変換されて表示されることになります。なお、typeという項目を追加すると型のチェックを自動で行われます。「型指定」を行うと、意図しない種類の値が入力された場合、Vue.jsがエラーとして警告をしてくれるようになります。

属性を定義する

var AbsoluteNumber = Vue.extend( {

  props: {
    value: {
      type: Number,
      default: 0
    }
  },

  template:
  '<div>{{ Math.abs( value ) }}</div>'
} );

あとは、親から属性に任意の数値を属性値として流し込むだけです。

value属性値を渡す

var AbsoluteNumber = Vue.extend( {

  props: {
    value: {
      type: Number,
      default: 0
    }
  },

  template:
  '<div>{{ Math.abs( value ) }}</div>'
} );

var componentRoot = new Vue( {

  template:
  '<div>' +
    '<absolute-number :value="100"></absolute-number>' +
    '<absolute-number :value="-50"></absolute-number>' +
    'hello!' +
  '</div>',

  components: {
    AbsoluteNumber: AbsoluteNumber
  }

} );

componentRoot.$mount( '.MyAppRoot' );

表示してみると、属性値に-50が設定された要素は、その絶対値50が表示されていることがわかります。

なお、属性のtypeNumberとしていますが、もちろんテキスト、真偽値も可能ですし、配列やオブジェクト、さらには関数を渡すこともできます。

まとめ

今回は、Vue.jsでのコンポーネントの基本を見てきました。次回以降は値の入出力、繰り返しや条件分岐を使ってみましょう。また、アニメーションについても解説する予定です。