vue.js 3のComposition APIを使ってコードの再利用をどのように行うかを確認していきます。本文書を読み終えるとcdnのvue.js3でのComposition APIの記述方法とComposition APIではどのようにコードの再利用を行うのかの基本を非常にシンプルな例から理解することができます。cdnを利用しているので動作確認も簡単に行うことができます。

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

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

ライフサイクルフックのマウント時に外部サーバにアクセスしてデータを取得する方法を確認します。外部サーバにはjsonplaceholerを利用します。

axiosを利用してデータ取得するのでvue同様にcdnを利用します。

Vue2ではライフサイクルフックmountedという名前でしたがVue3のComposition APIではmountedではなく”on”を先頭につけてonMountedとしてsetup関数の中で実行することができます。

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

Vue.refと同様にcdnを利用している場合はVue.onMountedとする必要があります。これでjsonoplaceholerから取得したデータが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適用後のテーブル

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

検索バーを追加して検索に入力した文字列が含む行のみ表示できるような検索機能を追加します。新たにリアクティブデータ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を忘れずに追加してください。


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を持つ行のみ表示されます。

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

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

検索を行った文字に一致する文字だけハイライトにする機能の追加を行います。Vue2では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することになります。これについてはまた別の機会に説明を行います。