Vue3 + Vuetify3でのv-alert関数式呼び出しの実装
【vue3-jp-admin】は長期的なプロジェクトであり、継続的に新しい技術を導入していきます。問題がある場合や開発に参加したい場合は、メール(yangrongwei1996@gmail.com)でご連絡ください。また、GitHubのissuesを通じて質問や提案をお寄せいただくことも可能です(https://github.com/yangrongwe/vue3-jp-admin/issues)。
皆さん、こんにちは!【vue3-jp-admin】にご興味を持っていただきありがとうございます。皆さんの「いいね」やコメントが、私の継続的な更新の励みになっています。
今回の実装内容は、Vue3でのv-alertの関数呼び出し機能です。デモはこちらからご覧いただけます。
機能概要
実装手順
以下に、Vue3 と Vuetify3 を使った v-alert の関数式呼び出し機能の実装手順をまとめます。
1. v-alertコンポーネントの封装
以下のコードで v-alert コンポーネントを封装します。
<template>
<div>
<div v-for="(alert, index) in alerts" :key="alert.id">
<v-alert
v-bind="alert.props"
:class="alertClasses"
:style="getTopStyle(index)"
@click="removeAlert(alert.id)"
>
{{ alert.message }}
</v-alert>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { VAlert } from 'vuetify/components';
import { useDisplay } from 'vuetify';
import { useAlertStore } from '@/store/alert';
export default defineComponent({
name: 'AlertComponent',
components: {
VAlert,
},
setup() {
const alertStore = useAlertStore();
const alerts = computed(() => alertStore.alerts);
const { mobile } = useDisplay();
const alertClasses = computed(() =>
mobile.value
? ['tw-absolute', 'tw-z-[9999]', 'tw-inset-x-[15px]', 'tw-w-auto']
: ['tw-absolute', 'tw-z-[9999]', 'tw-inset-x-[20%]', 'tw-w-auto']
);
const getTopStyle = (index: number) => {
const topPosition = 150 + index * 80;
return { top: `${topPosition}px` };
};
const removeAlert = (id: number) => {
alertStore.removeAlert(id);
};
return {
alertClasses,
alerts,
getTopStyle,
removeAlert,
};
},
});
</script>
<style scoped lang="scss"></style>
2. Pinia の封装
Pinia ストアの実装は以下の通りです。これにより、アラートの状態管理を簡単に行えます。
import { defineStore } from 'pinia';
interface Alert {
id: number;
message: string;
timeout: number;
props: Record<string, any>;
}
export const useAlertStore = defineStore('alert', {
state: () => ({
alerts: [] as Alert[], // アラートのリスト
}),
actions: {
// アラートを追加する
addAlert(alert: Omit<Alert, 'id'>) {
const id = Date.now();
const newAlert = { ...alert, id };
this.alerts.push(newAlert);
if (alert.timeout) {
setTimeout(() => {
this.removeAlert(id);
}, alert.timeout);
}
},
// 特定の ID のアラートを削除する
removeAlert(id: number) {
this.alerts = this.alerts.filter((alert) => alert.id !== id);
if (this.alerts.length === 0) {
this.$reset();
}
},
// すべてのアラートをクリアする
clearAlerts() {
this.alerts = [];
},
},
});
3. 呼び出しツールの封装
アラートを表示するためのツールを以下のコードで封装します。
import { createApp } from 'vue';
import AlertComponent from '@/components/JpAlert/index.vue';
import { useAlertStore } from '@/store/alert';
import vuetify from '@/plugins/vuetify/index.ts';
interface AlertOptions {
message: string;
timeout?: number;
props?: Record<string, any>; // v-alert の props
}
// グローバル変数の作成
let appInstance: ReturnType<typeof createApp> | null = null;
let mountPoint: HTMLElement | null = null;
export function showAlert(options: AlertOptions): void {
const { message, timeout, props = {} } = options;
if (!appInstance) {
// アプリケーションインスタンスの作成
appInstance = createApp(AlertComponent);
appInstance.use(vuetify);
// マウントポイントの作成とマウント
mountPoint = document.createElement('div');
document.body.appendChild(mountPoint);
appInstance.mount(mountPoint);
}
// Pinia で状態管理
const alertStore = useAlertStore();
alertStore.addAlert({ message, timeout, props });
}
export function clearAllAlerts(): void {
// Pinia で管理されている全てのアラートをクリア
const alertStore = useAlertStore();
alertStore.clearAlerts();
// アプリケーションインスタンスとマウントポイントをアンマウントし、削除
if (appInstance && mountPoint) {
appInstance.unmount();
document.body.removeChild(mountPoint);
appInstance = null;
mountPoint = null;
}
}
4. Hook の使用例
フックを使用してアラートを表示する方法は以下の通りです。
import { showAlert, clearAllAlerts } from '@/utils/dynamicAlert';
import { MainTitle } from '@/types/index';
const mainTitle: MainTitle = {
title: 'Vuetify3 アラート 二次封装【関数式呼び出し】',
linkText: 'src/views/componentsDemo/alert/index.vue',
path: 'https://github.com/yangrongwe/vue3-jp-admin/blob/main/src/views/componentsDemo/alert/index.vue',
};
const handleShowAlert = (btnIndex: number) => {
switch (btnIndex) {
case 1:
showAlert({
message: 'This is an alert default!',
props: {
closable: true,
},
});
break;
case 2:
showAlert({
message: 'This is an alert outlined!',
props: {
variant: 'outlined',
closable: true,
},
});
break;
case 3:
showAlert({
message: 'This is an alert tonal!',
props: {
variant: 'tonal',
closable: true,
},
});
break;
case 4:
showAlert({
message: 'This is an alert info!',
props: {
type: 'info',
closable: true,
border: 'start',
borderColor: 'white',
},
});
break;
case 5:
showAlert({
message: 'This is an alert success!',
props: {
type: 'success ',
closable: true,
border: 'top',
borderColor: 'white',
},
});
break;
case 6:
showAlert({
message: 'This is an alert warning!',
props: {
type: 'warning ',
closable: true,
border: 'bottom',
borderColor: 'white',
},
});
break;
case 7:
showAlert({
message: 'This is an alert error!',
props: {
type: 'error',
closable: true,
border: 'end',
borderColor: 'white',
},
});
break;
case 8:
showAlert({
message: 'This is an alert timeout!',
timeout: 3000,
props: {
type: 'info',
closable: true,
border: 'start',
borderColor: 'white',
},
});
break;
default:
break;
}
};
const handleClearAllAlerts = () => {
clearAllAlerts();
};
export { handleShowAlert, handleClearAllAlerts, mainTitle };
最後に関数式でのコンポーネントの封装はそれほど難しくありませんが、メモリリークに注意が必要です。例えば、clearAllAlerts で Vue インスタンスを削除するのを忘れると、メモリに不要なデータが残る可能性があります。今回の実装が皆さんのプロジェクトに役立つことを願っています!