フロントエンド

【Vue3/v-model】双方向データバインディングとは?

Vue3双方向データバインディング

reisuta

Webエンジニア | 20代中盤 | 大学時代はGmailすら知らないIT音痴でプログラミングとは無縁の生活を送る → 独学でプログラミングを学ぶ → Web系受託開発企業にエンジニアとして就職 → Web系自社サービス企業に転職 | 実務未経験の頃からVimを愛好しており、仕事でもプライベートでも開発はVimとTmuxを使っているので、VSCodeに疎いのが最近の悩み。何だかんだでやっぱりRubyが好き。

Vue.jsの概念に、
双方向データバインディングというものがあります。

これは、単方向データバインディングに対するものです。

本記事では、双方向データバインディングの解説を通して、
単方向データバインディングとの違いに触れつつ、
v-model, v-on, v-bindの関係性や違いについても解説します。

文字ではなく、動画で学習したい方は、
下記の動画御覧ください。

双方向データバインディングと単方向データバインディング

そもそも、データバインディングとは、何かっていうと、
これは、簡単に言うと、JavaScriptのデータと、
表示されているHTMLのデータとを合わせる仕組みのことです。

バックエンドとかだと、
あまりHTMLと合わせるみたいなシチュエーションがないので、
どちらかというと、フロントエンド技術の用語のような気もします。

さて、そんなデータバインディングですが、
バインディングされていないと、
Javascriptのデータをいくら内部で書き換えても、
画面の表示(HTML)が変わらないので、
データが変わっていることに気づけません。

イメージとしては、
Amazonのサイトとかで、
内部のJavaScriptで、商品の価格を1000円引きしているのに、
データバインディングされていないので、画面上では、
割引されていないように見えるような感じです。
(実際のapi処理では、JavaScriptのデータをバックエンドにわたすので、
仮に決済処理を行えば、割引価格になっているはずです)

そのため、画面を操るフロントエンドでは、
必須の技術なのですが、
こうしたバインディングにも、
単方向と、双方向という種類があります。

これらは、文字通りベクトルの違いです

フロントエンドのデータバインディングの場合、
大まかな流れとして

・JS → HTML
・HTML → JS

二種類があります。

双方向データバインディングとは、
この両方の流れをサポートしているバインディングのことです。

これに対して、単方向データバインディング、
上記のどちらか一方だけをサポートしているものです。

以下は、双方向データバインディングのサンプルコードです。

<template>
  <div>
    <label for="name">Name:</label>
    <input id="name" v-model="name" />
    <p>Hello, {{ name }}!</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const name = ref('');

</script>

これは、<input>タグの中身になにか入れると、
<p>タグの中身も同じものが表示されます。

これを実現しているのは、
v-modelというディレクティブなのですが、
(ディレクティブについては後で説明します)
流れとしては、下記のような感じです。

  1. <input>タグ(HTML) → name変数(JS)
  2. name変数(JS) → <p>タグ(HTML) 

このようにして、
双方向にデータがバインディングされています。

イメージとしては、データが一周しているような感じでしょうか。

ディレクティブとは?

さて次に、ディレクティブとはなんでしょう?

これは、HTMLで記述する、
v-から始まる属性のことです

ビルトインのディレクティブについては、
下記に記載があります。

https://ja.vuejs.org/api/built-in-directives.html

例えば、以下のようなものがあります。

v-bindバインディング機能
v-onイベント処理
v-model双方向データバインディング
v-htmlhtmlを表示する
v-ifif文
v-forfor文

他にも、v-onceとかv-preみたいなディレクティブもありますが、
使用頻度はあまり高くないので割愛します。
上記のビルトインディレクティブの一覧ドキュメントを御覧ください。

さて、この中で圧倒的によく使うのは、
v-bind、v-on、v-modelの3つで、
特にv-modelはよく使います。

以下、順番に解説します。

v-bind

先程、v-modelは双方向データバインディングだと説明しましたが、
v-modelの機能は、実はv-bindとv-onの複合で実現できます。

すなわち、v-bind単体だと、単方向データバインディングにあたるわけです。
それも方向的には、

JS → HTMLの方向です。
(v-onはこれの逆で、これらの複合で双方向データバインディングになります)

サンプルコードを紹介します。

<template>
  <div>
    <a :href="linkUrl">{{ linkText }}</a>
    <button :class="buttonClass" @click="onClick">{{ buttonText }}</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const linkUrl = ref('https://example.com');
const linkText = ref('Example Link');
const buttonClass = ref('primary');
const buttonText = ref('Click me!');

function onClick() {
  buttonText.value = 'Clicked!';
}

</script>

:classとか:hrefというのが、
v-bindの省略形の記法で、
v-bind:classとかと同じ意味です。

この場合、JS側で指定した、
linkUrlとかbuttonClass変数の内容が、
href属性やclass属性に適応されます。

v-on

v-onは、v-bindの逆の方向で、

HTML → JS

の流れです。(正確にはHTMLというよりDOMだが
templateタグからscriptタグという流れの表現的には、
HTMLと言っても良い気がしたのでHTMLとしています)

サンプルコードを紹介します。

<template>
  <div>
    <h1>タグ</h1>
    <input type="text" @input="onInput" :value="message" />
    <p>{{ message }}</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const message = ref('');

function onInput(event: Event) {
  const target = event.target as HTMLInputElement;
  message.value = target.value;
}

</script>

@inputというのは、
v-on:inputと同じ意味の省略形です。

inputイベントは、何かが入力されることをトリガーにします。
つまり、上記のコードは、inputタグ(HTML)に何かが入力されたら、
JS側のonInputメソッドが呼ばれ、message変数が書き換わるという、
バインディングをしています。

v-model

v-modelは、すでに出てきたv-bindとv-onの融合系みたいな感じです。

例えば、先程紹介した下記のコードですが、

<template>
  <div>
    <label for="name">Name:</label>
    <input id="name" v-model="name" />
    <p>Hello, {{ name }}!</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const name = ref('');

</script>

下記のように書き換えられます。

<template>
  <div>
    <label for="name">Name:</label>
    <input id="name" :value="name" @input="updateName" />
    <p>Hello, {{ name }}!</p>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const name = ref('');

function updateName(event: Event) {
  const target = event.target as HTMLInputElement;
  name.value = target.value;
}
</script>

記述が冗長になりましたが、同じことをしています。
イメージとしては、v-bind + v-on = v-modelみたいな感じです。

双方向データバインディングをしたい場合は、
基本的にv-modelを使うことが多いでしょう。

単方向データバインディングと双方向データバインディングどっちがいい?

これは、正直好みやケース・バイ・ケースの部分もあるのですが、
最近の主流は、やはり単方向データバインディングでしょうか。

というのも、双方向データバインディングの場合、
小規模のアプリとか、機能が単純なものであれば、
便利な代物ですが、複雑化かつ大規模になると、
データの流れが混戦しがちで、追うのも容易ではなくなってきます。

値があっちこっちにいったりきたりで、
結局どこ行った?状態になりがちなので、
双方向データバインディングは、一般的には、
メリットよりもデメリットのほうに注目されがちです。

また、意図せずデータが書き換わってしまうということも、
双方向データバインディングの場合、
単方向よりも起きやすい気がしています。

これも、あっちこっちにいってしまい、
追うことができなくなってしまうのが原因かと思います。

ただ、このあたりは、保守性が高いコードや設計、厳密なコーディングスタイルなどによって、
ある程度は解消できるものでもある気がするので、やはり正直チームやプロジェクトによると思います。

Reactは、基本的に単方向データバインディングを採用しており、
Reactと比較したVueのデメリットの一つとして、しばしば双方向データバインディングを
挙げられることも多いので、賛否両論の機能であることは間違いなさそうです。

Vue.jsにおいても、v-modelではなく、
v-bindやv-onだけを使えばいいのではないかという意見もありますが、
正直それだと、Vue.jsを採用するメリットが無い気がします。

また、v-modelを使わないとなると、
かなり記述が冗長になることが予想されます。

そのため、双方向データバインディングのデメリットが気になるのであれば、
Vue.jsの採用には慎重になったほうがいいかもしれません。

v-modelだけでなく、Vue.jsには、propsに加えemitというものもあり、
ここも双方向データバインディングのような動きをします。

しかし、逆にこのような双方向データバインディングのほうが、
記法をすっきりさせたり、柔軟な記述ができるという、
メリットの部分を重視すれば、Vue.jsを採用するのも有りだと思います。

まあ、結局は好き嫌いですね(笑)

ちなみに、Vue.jsのpropsとemitについては、
下記の記事で紹介しているので、よければご参考ください。

Vue3コンポーネント間通信
参考【Vue3 props/emit】コンポーネント間通信

コンポーネントとは? コンポーネントとは、そのままの意味としては、部品とか、構成要素とかといったものがあります。 Vue.jsにおけるコンポーネントという概念も、大まかにはそのような意味で、より具体的 ...

続きを見る

  • この記事を書いた人
  • 最新記事

reisuta

Webエンジニア | 20代中盤 | 大学時代はGmailすら知らないIT音痴でプログラミングとは無縁の生活を送る → 独学でプログラミングを学ぶ → Web系受託開発企業にエンジニアとして就職 → Web系自社サービス企業に転職 | 実務未経験の頃からVimを愛好しており、仕事でもプライベートでも開発はVimとTmuxを使っているので、VSCodeに疎いのが最近の悩み。何だかんだでやっぱりRubyが好き。

おすすめ記事はこちら

Vim/Neovimプラグイン 1

プラグインをどれだけ入れるかは、その人の思想なども関係するので、一概にこれがいいというのはないかもしれません。 プラグインを全く入れない人もいれば、100個以上入れる人もいます。 ただそれでも、これだ ...

VimとNeovimの比較 2

本記事では、VimとNeovimの違いについて、解説します。 VimとNeovimの違いについては、普段頻繁にVimなどを使う方でなければ、正直、あまり気にしなくてもいいかなと思います。 ただ、Vim ...

Ruby変数やすべてがオブジェクトについて 3

本記事は、Rubyの基礎文法である、変数や真偽値、論理演算子に触れると同時に、「すべてがオブジェクト」というRubyの特徴的な思想についても解説します。 この思想は、Rubyの文法の根幹になっているの ...

4

エンジニアにおすすめの技術書 書籍学習は、エンジニアの嗜みみたいなところがありますが、 良書というものは、意外とそこまで多くもありません。 そこで本記事では「技術書マニアの筆者が厳選した技術書20選」 ...

5

エンジニアになるには? プログラミングは、専門性が高く自分一人で勉強するのが大変に感じることも多いですよね。 そこで本記事では「おすすめのプログラミングスクール5選」を特徴と、現役エンジニア目線で優れ ...

-フロントエンド