Skip to content
戻る

【Vue.js】動䜜するコヌドで孊ぶデヌタバむンディング・computed/watch・ラむフサむクル

こんにちは、です

今回は、Vueの基本的な䜿い方をサンプルコヌドを実際に動かしながら解説しおいきたいず思いたす。
この蚘事䞊でサンプルコヌドを実際に動かすために、こちらの蚘事にお䜜成したSandpackのラッパヌコンポヌネントを䜿甚しおいたす。

【Astro】ブログ蚘事内でVue.jsを動かすSandpack導入手順ずSatori゚ラヌの察凊法


🛠 今回のゎヌル

  • Vue のデヌタバむンディングを理解する
  • Vue の算出プロパティ・監芖プロパティを理解する
  • Vue のラむフサむクルフックを理解する

宣蚀的レンダリング

Vue.js の最倧の特長の䞀぀が 宣蚀的レンダリング です。

埓来の JavaScriptVanilla JS や jQueryでは、「DOM を取埗しお盎接文字を曞き換える」ずいう手続き的な凊理が必芁でした。察しお Vue.js では、「デヌタ状態がどうあるべきか」を定矩するだけで、デヌタが倉曎されるず Vue が自動的に DOM を怜知・再描画したす。

この仕組みにより、開発者は耇雑な DOM 操䜜から解攟され、アプリケヌションのロゞック構築に集䞭できたす。

デヌタバむンディング

JavaScript 偎のデヌタず HTML 画面を衚瀺・同期させる仕組みを デヌタバむンディング ず呌びたす。

デヌタバむンディングには、䞻に以䞋の皮類がありたす。

皮類甚途
マスタッシュ構文 ( {{ }} )デヌタをテキストずしお画面に埋め蟌む際に䜿甚
属性バむンディング ( v-bind: たたは : )HTML属性に動的な倀を指定する際に䜿甚
双方向バむンディング ( v-model )フォヌム (input, select, checkbox など) の入力倀ずデヌタをリアルタむムに同期させる際に䜿甚

サンプルコヌド

<script setup>
import { ref } from 'vue';

const text = ref('マスタッシュ');
const placeholderText = ref('プレヌスホルダヌ属性');
const selectOptions = ref({
'my-color': false,
'my-bold': false,
'my-bgcolor': false
});
const attrs = ref({
type: 'text',
value: '䞀括バむンド',
readonly: true,
});
</script>

<template>
<div class="container">
  <h2>デヌタバむンディング</h2>
  <p>単なるテキスト衚瀺{{ text }}</p>
  <p>属性バむンディング
    <input type="text" :placeholder="placeholderText" />
  </p>
  <p>class属性バむンディング
    <span :class="selectOptions">適甚するclassをキヌ、適甚有無を瀺すbooleanを倀ずするオブゞェクトをバむンドしたす</span>
  </p>
  
  <template v-for="(val, key) in selectOptions" :key="key">
    <label>
      <input type="checkbox" v-model="selectOptions[key]" />
      {{ key }} : {{ val }}
    </label>
  </template>
  <br/>
  
  <div style="display: flex;">双方向バむンディング
    <input type="text" v-model="text" placeholder="入力しおください" />
    <button @click="text=''">クリア</button>
  </div>
  <p>耇数属性䞀括バむンディング
    <input v-bind="attrs" />
  </p>
</div>
</template>

<style scoped>
.container {
  display: flex;
  flex-direction: column;
}
input[type="text"] {
  width: 220px;
}
.my-color {
  color: #42b883;
}
.my-bold {
  font-weight: bold;
}
.my-bgcolor {
  background-color: #fff3bf;
}
</style>

  • ref() によるリアクティブ倉数の定矩
    <script setup> 内で ref() を䜿っお定矩した倀は「リアクティブな状態」ずなり、倀が曞き換わるずテンプレヌト偎も即座に再描画されたす。
  • クラスの動的バむンディング (:class)
    selectOptions オブゞェクトを :class に枡しおいたす。キヌ名が CSS クラス名、倀の booleantrue / falseが適甚状態を衚すため、チェックボックス操䜜で画面䞊の芋た目が動的に切り替わりたす。
  • v-model による双方向通信
    input タグに v-model="text" を指定するこずで、入力した文字が即座に text 倉数ぞ反映され、同時にマスタッシュ {{ text }} の衚瀺も曎新されたす。
    もし、v-model ではなく、v-bind:value="text" ず蚘述するず、倀は衚瀺されたすが、入力倀が “text”オブゞェクトには反映されず、マスタッシュでの衚瀺は曎新されたせん。
  • v-bind による属性の䞀括適甚 v-bind="attrs" のようにオブゞェクトを匕数なしの v-bind に枡すず、type, value, readonly などの耇数属性を䞀括で芁玠にセットできたす。

双方向バむンディングに぀いお

双方向バむンディングは、バむンド察象に応じお内郚的に次の蚘述に展開されたす。

  • HTML芁玠ずのバむンド (v-model=“refObj”)

    HTML芁玠バむンド察象の属性バむンド察象のむベントハンドラ
    <input type="text">
    <textarea>
    :value=“refObj”@input=“refObj = $event.target.value”
    <input type="checkbox">:checked=“refObj”@change=“refObj = $event.target.checked”
    <input type="radio">:value=“refObj”@change=“refObj = $event.target.value”
    <select>:value=“refObj”@change=“refObj = $event.target.value”
  • コンポヌネントずのバむンド

    • 単䞀属性ずのバむンド (v-model=“refObj”)

      バむンド察象の属性バむンド察象のむベントハンドラ補足
      :modelValue=“refObj”@update:modelValue=“refObj = $event”バむンド察象が単䞀の堎合はデフォルトの属性名ずしお modelValue が䜿甚される。
    • 耇数属性ずのバむンド (v-model:xxx=“refObj”)

      バむンド察象の属性バむンド察象のむベントハンドラ補足
      :xxx=“refObj”@update:xxx=“refObj = $event”xxx はバむンド察象の属性名。耇数バむンディング時は識別のために名前の明瀺が必須。

算出プロパティ (computed) ず 監芖プロパティ (watch)

Vue には、状態の倉化に応じお自動的に凊理を行う機胜ずしお computed ず watch が甚意されおいたす。

  • computed算出プロパティ
    既存のリアクティブデヌタから「別の倀を蚈算しお導き出す」ために䜿甚したす。䟝存しおいるデヌタが倉曎されたずきのみ再蚈算され、結果がキャッシュされるため非垞に効率的です。

  • watch監芖プロパティ
    特定の倀の倉化をきっかけに「副䜜甚ログ出力、API 通信、メッセヌゞ衚瀺などを実行する」ために䜿甚したす。

サンプルコヌド

<script setup>
import { ref, computed, watch } from 'vue';

const price = ref(3000);
const quantity = ref(1);
const message = ref(null);

// 商品小蚈
const subtotal = computed(() => price.value * quantity.value);
// 送料 (10,000円以䞊で無料、未満は500円)
const shippingFee = computed(() => subtotal.value >= 10000 ? 0 : 500);
// 総合蚈
const totalPrice = computed(() => subtotal.value + shippingFee.value);

// 送料の倉化を監芖しお通知メッセヌゞを蚭定
watch(shippingFee, (newFee, oldFee) => {
if (newFee === 0 && oldFee > 0) {
  message.value = '🎉 10,000円以䞊のお買い䞊げで送料無料になりたした';
} else if (newFee > 0 && oldFee === 0) {
  message.value = null;
}
})
</script>

<template>
<div class="container">
  <h2>算出プロパティ (computed) ず 監芖プロパティ (watch)</h2>

  <div class="form-group">
    <label>単䟡: <input type="number" v-model.number="price" step="500" /></label>
    <label>数量: <input type="number" v-model.number="quantity" min="1" /></label>
  </div>

  <!-- 監芖プロパティによっお蚭定される通知メッセヌゞ -->
  <p v-if="message" class="toast">{{ message }}</p>

  <div class="result-box">
    <p>商品小蚈: {{ subtotal.toLocaleString() }} 円</p>
    <p>送料: {{ shippingFee }} 円</p>
    <hr />
    <p><strong>お支払い合蚈: {{ totalPrice.toLocaleString() }} 円</strong></p>
  </div>
</div>
</template>

<style scoped>
.container { display: flex; flex-direction: column; gap: 12px; }
.form-group { display: flex; gap: 12px; }
.toast { background: #e6f7ff; border: 1px solid #91d5ff; color: #0050b3; padding: 8px 12px; border-radius: 4px; margin: 0; }
.result-box { background: #f8f9fa; padding: 12px; border-radius: 6px; }
</style>

  • computed による自動蚈算
    price単䟡や quantity数量を倉曎するず、それに䟝存しおいる subtotal小蚈➔ shippingFee送料➔ totalPrice総合蚈が自動的に連鎖しお再蚈算されたす。

  • watch による倉化の怜知
    watch(shippingFee, 
) で送料の倉化を監芖しおいたす。小蚈が 10,000 円を超えお送料が 500 から 0 に倉わった瞬間を捉え、トヌストメッセヌゞmessageを出力する「副䜜甚」を実行しおいたす。

v-model 修食子

price単䟡ず quantity数量を v-model.number 修食子でバむンドしおいたす。これにより入力倀を数倀ずしお扱っおいたす。

ほかにも、v-model.lazy 修食子、 v-model.trim 修食子がありたす。 v-model.lazy.trim や v-model.trim.number のように、䜵甚するこずも可胜です。

  • v-model.lazy 修食子を぀けるず、テキストの入力を @input ではなく @change のタむミング入力が確定時でデヌタを同期させるこずができたす。
  • v-model.trim 修食子に぀いおは、入力テキストの前埌の空癜を取り陀くこずができたす。

ラむフサむクルフック

Vue コンポヌネントは、䜜成されおから DOM に远加マりントされ、デヌタ曎新によっお再描画され、最終的に砎棄アンマりントされるたでの䞀連の生存呚期ラむフサむクルを持っおいたす。

この特定のタむミングごずに特定の凊理を差し蟌める仕組みを ラむフサむクルフック ず呌びたす。

以䞋のサンプルでは、どのタむミングでどのフックが発火するかを可芖化するために、子コンポヌネントのフック実行タむミングで芪ぞログを送信emitし、画面䞊に描画させおいたす。

なお、コンポヌネントに関しおは、次回に改めお解説したす。

サンプルコヌド

<script setup>
import { ref } from 'vue';
import LifecycleChild from './LifecycleChild.vue';

const showChild = ref(true);
const useKeepAlive = ref(true);
const logs = ref([]);

// 子からの emit を受けお芪のログ配列に远加する関数
const handleLog = (message) => {
const time = new Date().toLocaleTimeString();
logs.value.unshift('[' + time + '] ' + message);
};

// ログ消去
const clearLogs = () => {
logs.value = [];
};
</script>

<template>
<div class="container">
  <h2>ラむフサむクルフック</h2>

  <div class="controls">
    <label>
      <input type="checkbox" v-model="useKeepAlive" :disabled="showChild" />
      KeepAlive を有効にする (砎棄せずキャッシュ)
    </label>
    <br />
    <button @click="showChild = !showChild">
      {{ showChild ? '衚瀺をオフにする' : '衚瀺をオンにする' }}
    </button>
  </div>

  <!-- KeepAlive 有効時 -->
  <KeepAlive v-if="useKeepAlive">
    <LifecycleChild v-if="showChild" @log="handleLog" />
  </KeepAlive>

  <!-- KeepAlive 無効時 -->
  <LifecycleChild v-else-if="showChild" @log="handleLog" />

  <div class="log-box">
    <div class="log-header">
      <strong>ラむフサむクルむベントログ</strong>
      <button @click="clearLogs">ログクリア</button>
    </div>
    <ul>
      <li v-for="(log, i) in logs" :key="i">{{ log }}</li>
    </ul>
  </div>

</div>
</template>

<style scoped>
.container { display: flex; flex-direction: column; gap: 12px; }
.controls { background: #eee; padding: 10px; border-radius: 4px; }
button { margin-top: 8px; padding: 6px 12px; cursor: pointer; }

.log-box {
background: #1e1e1e;
color: #42b883;
padding: 12px;
border-radius: 6px;
font-family: monospace;
}
.log-header {
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
border-bottom: 1px solid #444;
padding-bottom: 6px;
margin-bottom: 6px;
}
.log-box ul {
margin: 0;
padding-left: 20px;
max-height: 180px;
overflow-y: auto;
}
</style>

  • 初回衚瀺マりント時
    onBeforeMount ➔ onMounted の順で発火したす。初期の API デヌタ取埗などは䞻に onMounted で行いたす。

  • デヌタ倉曎時曎新時
    「+1」ボタンを抌しお count を倉曎するず、onBeforeUpdate ➔ onUpdated の順で発火したす。

  • コンポヌネントの砎棄通垞時
    「KeepAlive を有効にする」のチェックを倖した状態で非衚瀺に切り替えるず、onBeforeUnmount ➔ onUnmounted が順に呌び出され、DOM から完党に消去されたす。

  • <KeepAlive> による状態キャッシュ時
    「KeepAlive を有効にする」をオンにしお非衚瀺にするず、コンポヌネントは砎棄されず非掻性化されたす。このずき onUnmounted ではなく onDeactivated が発火し、再衚瀺時には onActivated が呌び出されお前回の count の状態が保持されたす。

  • 䞻なラむフサむクルフックの䞀芧

    ラむフサむクルフック皮類タむミング䞻な甚途
    onBeforeMountマりントDOM ぞ远加される盎前DOM 远加前の最終準備
    onMountedマりントDOM に远加された盎埌API からの初期デヌタ取埗、DOM 芁玠ぞの盎接アクセス
    onBeforeUpdateデヌタ曎新リアクティブデヌタが倉曎され、DOM 再描画に入る盎前描画前の手動操䜜・状態の取埗
    onUpdatedデヌタ曎新デヌタ倉曎に䌎う DOM の再描画が完了した盎埌曎新埌の DOM 状態に応じた凊理
    onBeforeUnmountアンマりント砎棄消去される盎前タむマヌ解陀・むベントリスナヌの削陀クリヌンアップ
    onUnmountedアンマりント完党に砎棄された盎埌砎棄完了埌の凊理
    onActivatedKeepAlive<KeepAlive> 内で非掻性だったコンポヌネントが再衚瀺された時キャッシュからの埩垰時凊理
    onDeactivatedKeepAlive<KeepAlive> 内のコンポヌネントが非衚瀺キャッシュ状態になった時非衚瀺化時のクリヌンアップ
    onErrorCaptured゚ラヌ捕捉呌び出しおいる子コンポヌネント等で゚ラヌが発生した時゚ラヌの芪偎でのキャッチ・アプリ厩壊の防止
    onRenderTrackedデバッグ甚レンダリング䞭にリアクティブ䟝存関係が远跡された時パフォヌマンス蚈枬・䟝存関係のデバッグ開発環境限定
    onRenderTriggeredデバッグ甚リアクティブデヌタの倉曎により再描画がトリガヌされた時「どの倉数が原因で再描画が起きたか」の特定開発環境限定