索引

  • Vue.js アプリの作成
  • ディレクティブ
  • 算出プロパティ
  • ライフサイクルフック
  • リアクティブデータ
  • ウォッチャーによる明示的な監視

Vue.js アプリの作成

前回の最小構成にプラスアルファして、実際に Vue.js 経由で「Hello world」というメッセージを表示してみます。今回のコード以降は html 部分を適宜省略して書いていきます。

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <title>Vue app</title>
  </head>
  <body>
    <div id="app">
      <p>{{hello_message}}</p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.11/lodash.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script type="text/javascript">
      let helloapp = new Vue ({
        el: "#app" ,
        data: {
          hello_message: "Hello world" ,
        } ,
      }) ;
    </script>
  </body>
</html>

Vue.js の核となるのは、その名の通り Vue クラスです。Vue.js を起動するには、この Vue クラスをインスタンス化するだけです。

今回のサンプルでは、生成したインスタンスを後から参照できるように、変数 helloapp に格納していますが、参照する用途がなければ省略しても構いません。

Vue クラスのインスタンス化について見ていきます。

Vue (options) ;
// options: 動作オプション

引数 options には、Vue.js を動作させるためのオプションを「オプション名 : 値 , …」の連想配列もしくはハッシュ形式で指定していきます。利用できるオプションは様々ですが、上のアプリで使用しているのは以下の二つのオプションです。

el: "<Vue.js を適用する要素>" ,
data: データオブジェクト ,

el オプションは Vue.js を適用させる範囲を表します。今回のサンプルでは「#app」としているので id = “app” としている <div id=”app”></div>要素の配下で Vue.js が有効になります。有効範囲に html や body を指定するのは望ましくありません。

data オプションのデータオブジェクトは、テンプレートから参照できる値を格納します。

Vue.js ではアプリで利用する値をデータオブジェクトで用意しておいて、テンプレートからこれを参照する、という役割分担が基本です。このようなデータ割り当ての仕組みを「データバインディング」を言います。

テンプレートからデータオブジェクトいアクセスするには、{{…}} という構文を利用します。この構文を、Mustache 構文と言います。Mustache 構文には生の JavaScript のコードを記述することもできます。

ディレクティブ

Vue.js のテンプレートは標準的な HTML を拡張しただけのシンプルなものなので、覚えることも多くありません。テンプレート構文については以下の二点をおさえればいいです。

  • {{…}} Mustache 構文
  • v-xxxx 属性(ディレクティブ)

属性やスタイルの操作、条件分岐、繰り返し処理など、より複雑な場合はディレクティブを使用します。ディレクティブは、「v-〜」から始まる属性として表すのが基本です。

<a v-bind:href="url" >リンク</a>

文字列をテンプレートに埋め込む v-text=”〜”

データオブジェクトにアクセスするのに、{{…}} の代わりに v-text ディレクティブを利用することができます。

<div id="app">
  <p v-text="hello_message"></p>
</div>
<div id="app">
  <p>{{hello_message}}</p>
</div>

上のコードと下のコードは同義でどちらを使用しても構いませんが、コード全体で統一しておくことで、読みやすいコードになります。

属性値に JavaScript 式を埋め込める v-bind:〜

Vue.js アプリで以下のようなテンプレートは動作しません。

<a href="{{url}}">リンク</a>

html の属性値の中では、Mustache 構文は適用されないのです。しかし、上記の様に、a タグの href 属性にデータオブジェクトの、url をバインドさせたいという場面もあるでしょう。そういう時は、v-bind を使用します。

<a v-bind:href="url">リンク</a>
≈
<script>
  let app = new Vue ({
    ≈ ,
    data: {
      url: "https://google.com" ,
    } ,
  }) ;
</script>

v-bind はよく利用するという理由から、省略構文も用意されています。

<a :href="url">リンク</a>

上の様な省略形でも同じ様に href 属性の操作が可能です。

算出プロパティ

Mustache 構文を使えば、下のコードの様に、データ email の@より前の文字列を小文字に変換して表示することができます。

<div id="app">
  <p>{{email.split("@")[0].toLowerCase ()}}</p>
</div>

しかし、これは望ましくありません。複雑な式は本来見た目を表すべきテンプレートを読みづらくしてしまいます。テンプレートでは単純なプロパティ参照にとどめ、演算やメソッドの呼び出しはできるだけコードにさせるべきです。こういった場合に役に立つのが、算出プロパティです。

まずは例を見てください。

<div id="app">
  <p>{{local_email}}</p>
</div>
<script>
  let app = new Vue ({
    el: "#app" ,
    data: {
      email: "SAMPLE@email.com" ,
    } ,
    computed: {
      local_email: function () {
        return this.email.split ("@")[0].toLowerCase () ;
      } ,
    } ,
  }) ;
</script>

算出プロパティとは、既存のプロパティを演算した結果を取得するためのゲッターです。computed オプション配下に「プロパティ名 : 関数 ,」形式で定義します。

算出プロパティ内では、this.をつけることでデータオブジェクトを参照することができます。定義済みの算出プロパティへのアクセスは、データオブジェクト同様、Mustache 構文などを使用します。

算出プロパティは、メソッドとしても表すことができます。上の例をメソッドとして定義した場合は下の様になります。

<div id="app">
  <p>{{local_email ()}}</p>
</div>
<script>
  let app = new Vue ({
    el: "#app" ,
    data: {
      email: "SAMPLE@email.com" ,
    } ,
    methods: {
      local_email: function () {
        return this.email.split ("@")[0].toLowerCase () ;
      } ,
    } ,
  }) ;
</script>

computed で記述していた箇所を methods に書き換えただけなので、定義のコード自体は変わりません。ただし、今度はメソッドなので、呼び出すのに () を必要とします。

算出プロパティとメソッドの違いは、大きく三つあります。

一つ目は、算出プロパティは引数を持つことができなのに対し、メソッドは持つことができます。よって引数を伴う様な呼び出しには算出プロパティは使用できません。

二つ目は、用法です。算出プロパティの用途は、基本的に既存のデータの「加工を伴う取得」です。一方、メソッドはデータの取得に加え、操作や更新にも利用できます。代表的な例としては、マウスクリックなどに対応したイベント処理なども、メソッドの守備範囲です。要は、算出プロパティでできることは、メソッドでもできます。

ただし、引数を伴わない簡単な加工や演算なのであれは、算出プロパティを利用した方が、コードの意図は明確になります。

三つ目は、算出プロパティの値はキャッシュされるのに対し、メソッドの返り値は、キャッシュされないところです。メソッドは呼び出されるたびに評価されますが、算出プロパティはそれが依存するプロパティが変更された場合にのみ評価される様になっています。値を常に更新しなければならない場合はメソッドを使用する方が良いでしょう。

ライフサイクルフック

Vue.js インスタンスは、最初に生成された後、要素にマウントされて、データの変化に応じてビューを変化させていき、最終的に破棄されます。この様な生成から破棄までの流れを、ライフサイクルと言います。

Vue.js には各サイクル毎に呼び出されるメソッドが決まっています。これらメソッドのことを、ライフサイクルフックと呼びます。

Vue インスタンスの生成
イベント / ライフサイクルの初期化
beforeCreated ()
リアクティブデータの準備
created ()
テンプレートのコンパイル
beforeMounted ()
ページのマウント
mounted ()
描画
インスタンスの破棄
beforeDestroy ()
子コンポーネント / ハンドラーなどを破棄
destroyed ()
Vue インスタンスのライフサイクル
データの更新
beforeUpdate ()
再描画
updated ()
描画の詳細

ライフサイクルフックを利用した例を見ていきます。

<div id="app">
  ライフサイクルフック
</div>
<script>
  let app = new Vue ({
    el: "#app" ,
    beforeCreate: function () {
      console.log ("before create ...") ;
    } ,
    created: function () {
      console.log ("created ...") ;
    } ,
    beforeMount: function () {
      console.log ("before mount ...") ;
    } ,
    mounted: function () {
      console.log ("mounted ...") ;
    } ,
    beforeUpdate: function () {
      console.log ("before update") ;
    } ,
    updated: function () {
      console.log ("updated") ;
    } ,
    beforeDestroy: function () {
      console.log ("before destroy") ;
    } ,
    destroyed: function () {
      console.log ("destroyed") ;
    } ,
  }) ;
</script>

ライフサイクルフックは、メソッドとにていますが、別物です。methods オプションを同列に、Vue コンストラクタの動作オプションとして列記する点を間違えない様にしましょう。また、$destroyメソッドは、現在の Vue インスタンスを破棄するためのメソッドです。ライフサイクルフックという見地からは beforeDestroy メソッドおよび destroyed メソッドのトリガーとなります。ここでは、データオブジェクトの書き換えは発生していませんので、beforeUpdate メソッドと updated メソッドは呼び出されていないことも確認してください。

リアクティブデータ

Vue クラスの data オブジェクトに登録されたデータを、Vue.js の世界では、リアクティブデータと呼びます。リアクティブとは「反応できる」という意味です。データオブジェクトの変化を検知して、ページに自動的に反映させることから、この様に呼ばれています。また、リアクティブデータをを管理する Vue.js の仕組みをリアクティブシステムと言います。

これまでは、Vue コンストラクタに渡した値をそのままページに反映させていただけなので、あまりリアクティブであることを意識することはありませんでした。そこでもう少しだけリアクティブの恩恵を感じられる様な例を見つつ、Vue.js アプリを開発する上で陥りがちな罠について見ていきます。

下の例はリアクティブを意識したコードです。

<div id="app">
  <p>現在時刻 : {{current.toLocalString ()}}</p>
</div>
<script>
  let app = new Vue ({
    el: "#app" ,
    data: {
      current: new Date () ,
    } ,
    created: function () {
      let that = this ;
      this.timer = setInterval (function () {
        that.current = new Date () ;
      }, 1000) ;
    } ,
    beforeDestroy: function () {
      clearInterval (this.timer) ;
    } ,
  }) ;
</script>

ページに表示されている時刻は確かに1秒毎に更新されています。これがリアクティブである意味です。Vue.js では、data オブジェクトにデータを登録すると、そのすべてのプロパティを監視対象として登録します。そして、その変更を検知すると、自動的にビューに反映するわけです。

アプリ開発者はリアクティブシステムの内部的な挙動をほぼ意識する必要はありません。意識させないことが、Vue.js の存在意義な訳ですが、意識しないわけにもいきません。というのも、JavaSctipt の機能上の制約から、Vue.js はプロパティそのものの追加や削除を検知できないからです。よって以下のコードは正しく動作しません。

<div id="app">
  <p>筆者情報: {{author.name}} {{author.age}}</p>
</div>
<script>
  let app = new Vue ({
    el: "#app" ,
    data: {
      author: {
        name: "Yuto Hayashi" ,
      } ,
    } ,
    created: function () {
      let that = this ;
      this.timer = setTimeout (function () {
        that.author.age = 21 ;
      }, 1000) ;
    } ,
  }) ;
</script>

1000ms後に author.age プロパティは追加されますが、Vue.js がこれを検知できないため、ページは反映されないのです。この問題を解決する方法が二つあります。

一つ目は、すべてのプロパティを最初に準備しておく方法です。

最初に値が決まっていない場合にも、最低限空欄でプロパティを用意しておきます。これは Vue.js の制約を補うだけでなく、アプリで利用しているデータを data オブジェクトから一望できる、という意味でも有効な手法です。

二つ目は、プロパティの追加を Vue.js に通知する方法です。

Vue.set メソッドを利用することで、プロパティを追加するとともに、追加を Vue.js に通知できます。上の例を下の様に書き換えると後からでも author.age の追加は可能です。

<div id="app">
  <p>筆者情報: {{author.name}} {{author.age}}</p>
</div>
<script>
  let app = new Vue ({
    el: "#app" ,
    data: {
      author: {
        name: "Yuto Hayashi" ,
      } ,
    } ,
    created: function () {
      let that = this ;
      this.timer = setTimeout (function () {
        Vue.set (that.author, "age", "21") ;
      }, 1000) ;
    } ,
  }) ;
</script>

ウォッチャーによる明示的な監視

しばし、更新タイミングを手動で制御したいことがあります。例えば、入力値に応じて候補値をリスト表示するオートコンプリート機能を想定してみましょう。入力値はどんどん変化しているのに、都度、リスト取得のための問い合わせが発生するのは無駄です。

この様な場合には、入力の切れ目にだけ処理を実施する様にすることで、アプリの負荷を軽減できます。そして、その様な細かい制御は標準的なリアクティブシステムだけでは対応できないので、Vue.js ではより原始的な watch オブジェクトを提供しています。watch オプションを利用することで、データオブジェクトを特定のプロパティが変化したときに任意の処理を実行できます。

watch: {
  name: function (newValue, oldValue) {
    console.log (newValue, oldValue) ;
  } ,
} ,

ウォッチャーを一般化しましたが、わかりづらいので例を挙げてみます。

<div id="app">
  <input type="text" v-model="name" />
  <p>{{upper_name}}</p>
</div>
<script>
  let app = new Vue ({
    el: "#app" ,
    data: {
      name: "" ,
      upper_name: "" ,
    } ,
    watch: {
      name: _.debounce (function (newValue, oldValue) {
        this.upper_name = newValue.toUpperCase () ;
      }, 1000) ,
    } ,
  }) ;
</script>

name プロパティを変更したときに、すぐに入力値を大文字に変換する処理を呼び出さないのがポイントです。JavaSctipt の Lodashというライブラリの中の _.debounce を使用しています。具体的には、連続する呼び出しを無視し、最後呼び出されてから1000ms以上呼び出しがなかった場合にのみ処理が走る様にしています。Lodash の _.debounce メソッドは、処理を直接実行するのではなく、そのための関数を返す点に注意してください。watch オプションを computed オプションに変えると即座に値が変化することも確認してください。

computed: {
  upper_name: function () {
    return this.name.toUpperCase () ;
  } ,
} ,

次回があれば…

次回

  • ディレクティブ

前回


林 裕大Hayashi Yuto
麻雀 / コーディング / 東方 / V-Tuber / 書道 /
  • CentOS
  • AmazonWebServices
  • SQLite
  • Docker
  • Python
  • Ruby
  • PHP
  • Laravel
  • WordPress
  • Nodejs
  • JavaScript
  • Sass
  • HTML5
  • Bootstrap
  • adobe Illustrator
  • adobe Photoshop
お問い合わせ

大変申し訳ありませんが、現在お問い合わせフォームは機能しておりません。お手数ですが、お問い合わせの際は、下記アドレスまでよろしくお願いいたします。

h3yukomah1y1k1m@gmail.com