LoginSignup
4
2

More than 3 years have passed since last update.

Vue3 teleport と emits オプションを試してみる

Last updated at Posted at 2021-01-13

Prologue

WEB+DB PRESS Vol.120 で Vue3の特集がされていました。
内容は初学者でもわかりやすいチュートリアル形式になっているので、これから Vue を始める方でもおすすめだと思います。
その中に Vue3 で出た新しい機能について書かれていたので、今回はそこにフォーカスして試してみました。

参考

WEB+DB PRESS Vol.120

環境

  • macOS: v10.15.6
  • node.js: v12.18.2
  • terminal: iTerm
  • エディタ: VS Code
  • パッケージマネージャ: yarn

<teleport> コンポーネント

Vue の組み込みコンポーネントで、テンプレートに記述したコンテンツをページ内の任意の箇所に移動させることができます。

=> これによりz-index の回避や header の調整などが可能になる...

実感を得るために、実際に動かしてみます。

サンプルプロジェクトを作成

プロジェクト名は vue3-prj として作成していきます。
version は Vue3 を選択しますが、それ以外はお好みで大丈夫かと思います。

vue create vue3-prj 


Vue CLI v4.5.4
┌─────────────────────────────────────────────┐
│                                             │
│    New version available 4.5.4 → 4.5.10     │
│   Run yarn global add @vue/cli to update!   │
│                                             │
└─────────────────────────────────────────────┘

? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, TS, Router, Linter
? Choose a version of Vue.js that you want to start the project with 3.x (Preview)
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? No


Vue CLI v4.5.4
✨  Creating project in /Users/mi**/mii_work/vue3-prj.
🗃  Initializing git repository...
⚙️  Installing CLI plugins. This might take a while...

yarn install v1.22.4
info No lockfile found.
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...


success Saved lockfile.
info To upgrade, run the following command:
$ brew upgrade yarn
✨  Done in 57.46s.
🚀  Invoking generators...
📦  Installing additional dependencies...

yarn install v1.22.4
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Saved lockfile.
✨  Done in 13.49s.
⚓  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project vue3-prj.
👉  Get started with the following commands:

 $ cd vue3-prj
 $ yarn serve

/components/Modal.vue を作成し、シンプルなモーダルを作成します。

/components/Modal.vue
<template>
  <button @click="openModal">Open</button>
  <teleport to="body">
    <div v-if="isOpenModal" class="modal-content">
      <p>
        This is Modal!!!!!
      </p>
      <p>message: {{ message }}</p>
      <button @click="closeModal">Close</button>
    </div>
  </teleport>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  name: "Modal",
  props: {
    message: {
      type: String
    }
  },
  setup() {
    const isOpenModal = ref(false);
    const openModal = () => {
      return (isOpenModal.value = true);
    };
    const closeModal = () => {
      return (isOpenModal.value = false);
    };

    return {
      openModal,
      closeModal,
      isOpenModal
    };
  }
});
</script>

こちらを /views/Home.vue の以下の位置に設置します。

/views/Home.vue
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <Input @send="emitSend" />
    <Modal :message="message" />
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import Input from "@/components/Input.vue";
import Modal from "@/components/Modal.vue";

export default defineComponent({
  name: "Home",
  components: {
    Input,
    Modal
  },
  setup() {
    const message = ref("");
    const emitSend = (value: string) => {
      message.value = value;
    };
    return {
      emitSend,
      message
    };
  }
});
</script>

body に宛先を向けた場合、モーダルを開くボタンをクリックすると、DOM 上では画像の位置に表示されます。

スクリーンショット 2021-01-07 13.57.41.png

emits オプション

コンポーネントがどのように機能するかをより適切に文書化するために、発行されたすべてのイベントを定義することをお勧めします。

参考:
- https://v3.vuejs.org/guide/component-custom-events.html#defining-custom-events
- https://v3.ja.vuejs.org/api/options-data.html#emits

記法としては以下の2通りあるとのことなので、こちらも試してみます。

  1. カスタムイベント名を配列で列挙する方法
  2. オブジェクトで定義してデータの有効性をチェックする関数を追加、データの有効性をチェックする方法

/components/Input.vue を作成し、イベントを発火させます。
コンポーネントの設置箇所は上記 /views/Home.vue 内に記載していますのでご参考ください。

/components/Input.vue
<template>
  <div class="input-content">
    <input v-model="message" type="text" />
    <button @click="sendText">emit</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";

export default defineComponent({
  name: "Input",
  emits: {
    send(message: string) {
      return message.length > 4
    }
  },
  setup(props, { emit }) {
    const message = ref('');
    const sendText = () => {
      emit("send", message.value);
    };

    return {
      sendText,
      message
    };
  }
});
</script>

引数 message は string 型、長さは4文字以下だとエラーになるように記載しました。
挙動は以下のようになります。

warning

  • emits オプションを指定していない場合 -> warning が出ます
runtime-core.esm-bundler.js?5c40:38 [Vue warn]: Component emitted event "send-text" but it is neither declared in the emits option nor as an "onSend-text" prop.

推奨レベルなので warning として出るのかな、と思います。

  • emits オプションを指定して、オプションの条件に添わない値(3文字など)を入力すると warning が出ます。
runtime-core.esm-bundler.js?5c40:38 [Vue warn]: Invalid event arguments: event validation failed for event "send".

Epilogue

emits オプションはもう少しがっつり弾かれるのかとおもったのですが、warning で出ていました。
文書化が目的だからでしょうか。コードや可読性の向上以外にも使えそうなので、引き続き調べてみます。
fragments も活用してみましたが、特に意識しないで書けること、もちろんエラーにもならないので、個人的には書きやすくなったなと思いました。
teleport はもう少し複雑な構成だとより恩恵を感じるのかなと感じました。ID 指定などもできるそうなので、試してみたいと思います。
今回はそれぞれ感触を確かめるに留まりましたが、時間があれば実践的なコードを作ってみようと思います。

何かご指摘などありましたらご連絡ください。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2