Vue 3のComposition APIを使ってコードの再利用をどのように行うかを確認していきます。再利用できそうな機能を取り出すことで同じコードを何度も記述する必要がなくなり効率的にコードを記述することができます。本文書を読み終えるとcdnのVue 3を使ったComposition APIの記述方法とComposition APIではどのようにコードの再利用を行うのかの基本を非常にシンプルな例から理解することができます。cdnを利用しているのでVue環境を構築することなく動作確認も簡単に行うことができます。Composition APIではVue 2の時よりも関数の再利用が簡単だということを理解してもらえると思います。

Composition APIを利用しなかった場合のコードについては下記の文書で説明を行っています。Composition API以外のコードに関する不明点がある場合は下記の文書を参考にしてください。

cdnを使ってHello World

Vue CLIコマンドを使ってVue3の環境を構築することは可能ですが、すぐに動作確認を行いたい場合はcdnを利用するのが一番楽です。


<script src="https://unpkg.com/vue@next"></script>

Composition APIを利用してコードを記述していくと下記のように記述することができます。Vueインスタンスの作成方法もnew Vue()からVue.createAppに変わっています。インスタンスの作成方法についてはComposition APIを利用しない場合も同様です。

setup関数の中にリアクティブなデータや関数を記述していきます。リアクティブなデータはrefまたはreactiveメソッドのどちらからでも作成することができますが今回はrefを利用します。cdnを使用する場合はrefの前に必ずVueをつけてください。setup内で記述したリアクティブなデータや関数は最後にreturnを行う必要があります。


<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>Vue 3</title>
  </head>
  <body>
    <div id="app">
      <h1>{{ message }}</h1>
    </div>
    <script src="https://unpkg.com/vue@next"></script>
    <script>
      Vue.createApp({
        setup() {
          const message = Vue.ref("Hello World");
          return {
            message,
          };
        },
      }).mount("#app");
    </script>
  </body>
</html>

ブラウザで確認すると画面にはHello Worldが表示されます。

cdnのvue3でHello world
cdnのvue3でHello world

ライフサイクルフックでデータを取得

ライフサイクルフックのマウント時に外部サーバにアクセスしてデータを取得する方法を確認します。外部サーバにはJSONPlaceHolderを利用します。JSONPlaceHolderはダミーのJSONデータを戻してくれる無料のサービスです。動作確認などに利用することができます。

axiosを利用してJSONPlaceHolderからデータの取得を行います。axiosもvue同様にcdnを利用します。

Vue 2ではmountedという名前でしたがVue3のComposition APIではmountedではなく”on”を先頭につけてonMountedとしてsetup関数の中で実行することができます。

usersはrefを使って設定を行い、初期値は空の配列[]を設定しています。以前はサーバから取得したデータはthis.usersを利用していましたが、thisは必要ありません。しかし、refを利用している場合はusers.valueとvalueをつける必要があります。

Vue.refと同様にcdnを利用している場合はVue.onMountedとする必要があります。これでJSONPlaceHolderから取得したデータがusersの中に保存されます。messageとreturnするオブジェクトの中にusersを含める必要があります。


Vue.createApp({
  setup() {
    const message = Vue.ref("Hello World");
    const users = Vue.ref([]);

    Vue.onMounted(() => {
      axios
        .get("https://jsonplaceholder.typicode.com/users")
        .then((response) => (users.value = response.data));
    });
    return {
      message,
      users,
    };
  },
}).mount("#app");

HTML側でのusersの展開方法v-forは以前と同じで下記のように記述することができます。


<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Website</th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="user in users" :key="user.id">
      <td>{{ user.name }}</td>
      <td>{{ user.email }}</td>
      <td>{{ user.website }}</td>
    </tr>
  </tbody>
</table>

ここまで記述しブラウザで確認するユーザ一覧が表示されます。

ユーザ一覧を表示
ユーザ一覧を表示

styleタグを使ってテーブルにCSSを適用します。


<style>
  table {
    border-collapse: collapse;
    width: 100%;
  }

  td,
  th {
    border: 1px solid #dddddd;
    text-align: left;
    padding: 8px;
  }

  th {
    color: white;
    background-color: #1e90ff;
  }
</style>
CSS適用後のテーブル
CSS適用後のテーブル

Composition APIではsetup関数の中でref関数を利用することでリアクティブなデータが作成できることがわかりました。templateタグ側のブラウザへの描写に関する処理は以前のOptions APIと同じであることもわかりました。

Vue 2までの記述方法をOptions APIと呼びます。Vue 3でもCompositon APIを利用せずOptions APIで記述することは可能です。

検索(フィルター)機能の追加

検索バーを追加して検索に入力した文字列が含む行のみ表示できるような検索機能を追加します。新たにリアクティブデータsearchを追加します。


setup() {
  const message = Vue.ref("Hello World");
  const users = Vue.ref([]);
  const search = Vue.ref([]);

  Vue.onMounted(() => {
    axios
      .get("https://jsonplaceholder.typicode.com/users")
      .then((response) => (users.value = response.data));
  });
  return {
    message,
    users,
    search
  };
},

検索バーを追加します。v-modelの設定などhtml側での設定はVue2の時と同じです。


<div>検索:<input type="text" v-model="search" /></div>

computedプロパティを利用して検索を行いますが、computed関数を利用します。こちらもcdnではVueをつける必要があります。returnにも必ず追加した変数updated_usersを忘れずに追加してください。updated_usersのcomputed関数の中ではfilterを利用してusersの中から一つずつuserデータを取りだname, email, websiteプロパティの中にsearchWordが含まれているかincludesメソッドを使ってチェックしています。includesメソッドは含まれる文字がある場合にはtrueを戻し、ない場合にはfalseを戻します。


const updated_users = Vue.computed(() => {
  let searchWord = search.value.trim();

  if (searchWord === "") return users.value;

  return users.value.filter((user) => {
    return (
      user.name.includes(searchWord) ||
      user.email.includes(searchWord) ||
      user.website.includes(searchWord)
    );
  });
});

// 略
return {
  message,
  users,
  search,
  updated_users,
};

ブラウザで確認すると検索バーに文字列を入れると入力した文字にひっかかる文字列を持つ行のみ表示されることが確認できます。

Lを入力するとLを持つ行のみ表示されます。

検索バーに文字を入力
検索バーに文字を入力

検索文字のハイライト機能追加

検索を行った文字に一致する文字だけハイライトにする機能の追加を行います。Vue 2ではmethodsの中に関数を記述していましたがComposition APIはJavaScriptの通常の関数としてsetup関数の中に記述することができます。

追加するhighLight関数の中では、検索文字と一致する文字を引数から与えられる文字の中にあるかどうかチェックを行い、その文字がある場合はCSSを適用して戻すというシンプルな内容です。


const highLight = (text) => {
  let searchWord = search.value.trim();

  if (searchWord === "") return text;

  if (!text.includes(searchWord)) return text;

  const re = new RegExp(searchWord, "ig");

  return text.replace(re, function (search) {
    return (
      '<span style="background-color:yellow;font-weight:bold">' +
      search +
      "</span>"
    );
  });
};
//略
return {
  message,
  users,
  search,
  updated_users,
  highLight,
};

追加したhighLight関数はv-htmlを利用して下記のように設定を行います。


<tr v-for="user in updated_users" :key="user.id">
  <td v-html="highLight(user.name)"></td>
  <td v-html="highLight(user.email)"></td>
  <td v-html="highLight(user.website)"></td>
</tr>

設定の結果先ほどと同様に”L”の文字を入力すると一致しる文字だけ背景が黄色になります。

検索文字に一致した文字のみハイライト
検索文字に一致した文字のみハイライト

ハイライト機能の取り出し

ハイライト機能は引数から受け取った文字列が入力した文字列と一致するかどうかをチェックする機能であるため他でも再利用できる可能性があります。

再利用できるようにハイライトの機能のコードのみ取り出してみましょう。highLightTextという名前をつけて取り出しを行いました。この関数は他のComposition APIでも利用することが可能です。


const hightLightText = () => {
  const search = Vue.ref("");

  const highLight = (text) => {
    let searchWord = search.value.trim();

    if (searchWord === "") return text;

    if (!text.includes(searchWord)) return text;

    const re = new RegExp(searchWord, "ig");

    return text.replace(re, function (search) {
      return (
        '' +
        search +
        ""
      );
    });
  };

  return {
    search,
    highLight,
  };
};

取り出したhighLightTextを元のComposition APIから利用するために下記のように記述します。


Vue.createApp({
  setup() {
    const message = Vue.ref("Hello World");
    const users = Vue.ref([]);
    const { search, highLight } = hightLightText();

    const updated_users = Vue.computed(() => {
      let searchWord = search.value.trim();

      if (searchWord === "") return users.value;

      return users.value.filter((user) => {
        return (
          user.name.includes(searchWord) ||
          user.email.includes(searchWord) ||
          user.website.includes(searchWord)
        );
      });
    });

    Vue.onMounted(() => {
      axios
        .get("https://jsonplaceholder.typicode.com/users")
        .then((response) => (users.value = response.data));
    });
    return {
      message,
      users,
      search,
      updated_users,
      highLight,
    };
  },
}).mount("#app");

再利用可能な関数として取り出すことができ、取り出す前と同じように動作することが確認できます。

検索文字に一致した文字のみハイライト
機能を分けても動作は同じ

cdnを利用しているため取り出した関数を別のファイルとしてimportすることはできませんが、Vueのプロジェクトであれば別のファイルを作成しそのファイルをimportする形で利用することができます。別のプロジェクトや場所で利用したい場合はこのファイルをimportすることになります。これについてはまた別の機会に説明を行います。