HTMLにちょい足しでできる! Vue.jsでサクッと動きをつける方法

はてなブックマークへ投稿
98
Xへポスト
78
URLをコピー
共有

Vue.jsは昨今よく使われるモダンなJavaScriptフレームワークの1つです。

実は、Vue.jsは<script>タグから読み込めばNode.jsを用いずとも使えます。この方法ならばLP(ランディング・ページ)やWordPressサイトといったHTMLベースのサイトでもVue.jsを活用できるでしょう。本記事ではそのようなちょい足しVue.jsの活用例を、Vue 3のComposition APIとESモジュールを使って紹介します。

Vue.jsをちょい足す

HTMLに下記のようなコードを足すことで、Vue.jsを<div id="app">の要素内に導入できます。Vue.jsの読み込みにはインポートマップを使っています。

<div id="app">
  <!-- Vue.jsを使いたい部分 -->
</div>
<script type="importmap">
  {
    "imports": {
      "vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.5.44/vue.esm-browser.prod.js"
    }
  }
</script>
<script type="module">
  import { createApp } from "vue";
  const app = createApp({
    /* Vue.jsの各種処理 */
  });
  app.mount("#app");
</script>

上記はHTML内に直接JavaScriptを記述していますが、下記のように外部ファイルとしても利用できます。

<div id="app">
  <!-- Vue.jsを導入したい部分 -->
</div>
<script type="importmap">
  {
    "imports": {
      "vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.5.44/vue.esm-browser.prod.js"
    }
  }
</script>
<script type="module" src="./script.js"></script>

▼script.js

import { createApp } from "vue";
const app = createApp({
  /* Vue.jsの各種処理 */
});
app.mount("#app");

いずれもESモジュールを用いて読み込んでいます。使用する場合は<script type="module">のようにtype属性を付与する必要があります。

ESモジュールを使うと、JavaScriptをモジュール単位で管理できます。Vue.jsの各種関数も、import文で必要なものを読み込めます。インポートマップやESモジュールについては下記の記事にて詳しく解説しています。

これでVue.jsの記法や機能が使えるようになりました。

実際にちょい足しVue.jsを実装してみる

入力値から合計を自動計算するフォーム

computed()を使うと、フォームの入力に合わせて計算結果を自動で更新できます。その例として、大人・子どもの人数から入場料を計算するフォームを作りました。

入場料を自動計算するフォーム

v-model.numberで、入力した人数をVue.jsの値と同期します。.numberを付けると、入力値を数値として扱えます。

<div id="app" v-cloak>
  <label>大人<input v-model.number="adult" />人</label>
  <label>子ども<input v-model.number="children" />人</label>
  <p>合計:{{ entryFee }}円</p>
</div>

JavaScriptでは、人数をref()、合計金額をcomputed()で管理します。

import { ref, computed, createApp } from "vue";
const app = createApp({
  setup() {
    // 入場料の定数
    const ADULT_FEE = 1400;
    const CHILD_FEE = 800;
    // 入力した人数です
    const adult = ref(0);
    const children = ref(0);

    const entryFee = computed(
      () => ADULT_FEE * adult.value + CHILD_FEE * children.value,
    );
    return { adult, children, entryFee };
  },
});
app.mount("#app");

※コードは説明に必要な部分だけを抜粋しています。

computed()に計算式を書いておくだけで、人数を変えるたびに合計と画面の表示が自動で更新されます。

チェックボックスの状態を監視するフォーム

ここでは3つ以上チェックしないと送信ボタンが押せないサンプルを用意しました。

チェックボックスに3つ以上チェックをいれないと送信ボタンが押せない

<div class="check">
  <label
    ><input
      type="checkbox"
      value="react"
      v-model="checkBoxState"
    />React</label
  >
  <label
    ><input
      type="checkbox"
      value="angular"
      v-model="checkBoxState"
    />Angular</label
  >
  ...
  <!-- 省略 -->
</div>
<button type="button" :disabled="!isButtonEnabled">
  送信
</button>

v-modelは、複数のチェックボックスにも使えます。

import { computed, createApp, ref } from "vue";

const app = createApp({
  setup() {
    // チェックボックスの状態です
    const checkBoxState = ref([]);

    // ボタンが押せるかどうか
    const isButtonEnabled = computed(() => checkBoxState.value.length >= 3);
    return { checkBoxState, isButtonEnabled };
  },
});

※コードは説明に必要な部分だけを抜粋しています。

複数のチェックボックスの状態管理もref()を使って簡単に実装できます。v-modelを使うと、チェックした項目の値が自動的に配列に入ります。

computed()でチェック数が3以上かを判定し、:disabledでボタンの有効・無効を切り替えます。

ページネーションのある一覧

さいごにAPIから取得してきたデータをページネーションで表示する、少し複雑な例を紹介します。

ページネーションつきの一覧。進むボタン、戻るボタンで切り替わる

<div id="app">
  <div v-if="books.length">
    <transition name="fade" mode="out-in">
      <ul :key="pagination">
        <li v-for="book in currentBooks" :key="book.id">
          <img
            :src="book.thumbnail"
            :alt="book.name"
            width="300"
            height="200"
          />
          <h2>{{ book.name }}</h2>
          <p>{{ book.author }}</p>
        </li>
      </ul>
    </transition>
    <p>{{ pagination }} / {{ totalPages }}</p>
    <p>
      <button @click="decrementPagination" :disabled="pagination <= 1">
        戻る
      </button>
      <button
        @click="incrementPagination"
        :disabled="pagination >= totalPages"
      >
        進む
      </button>
    </p>
  </div>
</div>

※コードは説明に必要な部分だけを抜粋しています。

大まかな構成は以下のようになっています。

  • onMounted()でJSONファイルから書籍データを取得する。
  • computed()で現在のページに表示する分だけデータを切り出す。
  • v-forで書籍の一覧を表示する。

JavaScriptの実装は、GitHubのコードを参照してください。

<transition name="fade" mode="out-in">
  <ul :key="pagination">
    <li v-for="book in currentBooks" :key="book.id">
      <img :src="book.thumbnail" :alt="book.name" width="300" height="200" />
      <h2>{{ book.name }}</h2>
      <p>{{ book.author }}</p>
    </li>
  </ul>
</transition>

computed()で表示するデータを計算しておけば、ページが変わるとv-forの一覧も自動で更新されます。

<ul>の:keyにページ番号を指定すると、ページごとに一覧が差し替わります。<transition>コンポーネントのmode="out-in"で、前の一覧がフェードアウトしてから次の一覧をフェードインさせます。フェード用のCSSはサンプルの共通CSSをご覧ください。

:disabledで先頭ページの戻るボタンと、最終ページの進むボタンを無効化します。関数内のif文でも範囲を確認し、連打しても存在しないページへ移動しないようにしています。

ページネーションによる切り替え機能をもった一覧表示は、素のJavaScriptで作ろうとすると、ページの状態管理、内容の書き換えなど大変です。それら面倒なことはVue.jsを活用すれば低コストで実装可能です。

Vue.jsをちょい足すメリット

ちょい足しVue.jsには以下のようなメリットがあります。

  • 状態を持たせることができるので、状態に応じた表示の切り替え・処理の切り替えが低コストで実装できる
  • 表示に関する処理をVue.js側に任せられるので、開発者はロジックに集中できる
  • PHPやプレーンなHTMLで作られたページにも追加できる

jQueryとの比較

ウェブサイトに導入してJavaScript開発を容易にするものとして、jQueryがあります。Vue.jsと設計思想などが違うので単純な比較はできませんが、以下のような差があります。

jQueryのメリット

  • 豊富なプラグインが存在し、それを利用した開発ができる
  • slideDown()などのアニメーションメソッドがあらかじめ用意されているので、アニメーションの実装が楽
  • 平易に書け、学習コストも低く、普及度も高い

Vue.jsのメリット

  • 状態管理が容易にできるので、状態の違いによる処理の実装が楽
  • 表示の制御をテンプレート構文を使って実現できるので、ロジックの分離ができる
  • v-modelでフォームの入力値と状態を同期できるのでインタラクティブな処理が得意

ちょい足しVue.jsの注意点

Vue.jsが実行されるまで表示制御が効かないため、非表示にしたい要素が一瞬見えてしまうことがあります。また、マスタッシュ記法などのVue.js独自の書き方がプレーンテキストとして出力されてしまいます。

読み込み前にマスタッシュ記法が見えてしまっている様子

※動画では分かりやすくするため、読み込み速度を落として実行しています

それらを防ぐためにはv-cloakというVue.jsの組み込みの属性を使って非表示にしておく方法があります。

<div id="app" v-cloak>
  ...
</div>
[v-cloak] {
  visibility: hidden;
}

読み込み前にマスタッシュ記法が見えないように対応

Vue.jsが実行されるとv-cloakは自動的に外れ、表示されます。それまではコンテンツが隠れるため、v-cloakは必要な範囲だけに付けましょう。

サンプルで使用しているVue 3.5は、Gzip圧縮時で約64KB(元ファイルは約175KB)です。容量的に問題になることはないでしょう。

まとめ

Vue.jsというとSPA(シングル・ページ・アプリケーション)のためのツールというイメージがあるかもしれませんが、このようにウェブサイトに動的要素をサクッと導入する使い方も可能です。状態の管理も比較的容易に実装できます。

Vue.jsは簡単に追加できるので、これを機会にちょい足ししてみてはいかがでしょうか?

※この記事が公開されたのは5年前ですが、今月10月に内容をメンテナンスしています。

Google検索でICS MEDIAの記事を見つけやすくしよう

優先するソースに追加すると、GoogleのトップニュースやAI検索でICS MEDIAの記事が表示されやすくなります。

Googleで優先するソースとして追加
SNSでシェアしよう
シェアいただくと、サイト運営の励みになります!
はてなブックマークへ投稿
Xへポスト
URLをコピー
共有
西原 翼

建築関係出身のインタラクションデザイナー。デザインとエンジニアリングのつながりを探求したい。現実と虚構の狭間も好き。趣味はCG、工作、料理など。

この担当の記事一覧
新着記事のお知らせCSSだけでつくる! スクロールトリガーアニメーション