Prologue
WEB+DB PRESS Vol.120 で Vue3の特集がされていました。
内容は初学者でもわかりやすいチュートリアル形式になっているので、これから Vue を始める方でもおすすめだと思います。
その中に Vue3 で出た新しい機能について書かれていたので、今回はそこにフォーカスして試してみました。
参考
環境
- 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
を作成し、シンプルなモーダルを作成します。
<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
の以下の位置に設置します。
<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 上では画像の位置に表示されます。
emits オプション
コンポーネントがどのように機能するかをより適切に文書化するために、発行されたすべてのイベントを定義することをお勧めします。
参考:
- https://v3.vuejs.org/guide/component-custom-events.html#defining-custom-events
- https://v3.ja.vuejs.org/api/options-data.html#emits
記法としては以下の2通りあるとのことなので、こちらも試してみます。
- カスタムイベント名を配列で列挙する方法
- オブジェクトで定義してデータの有効性をチェックする関数を追加、データの有効性をチェックする方法
/components/Input.vue
を作成し、イベントを発火させます。
コンポーネントの設置箇所は上記 /views/Home.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 指定などもできるそうなので、試してみたいと思います。
今回はそれぞれ感触を確かめるに留まりましたが、時間があれば実践的なコードを作ってみようと思います。
何かご指摘などありましたらご連絡ください。