ホームページ >ウェブフロントエンド >Vue.js >Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう
管理バックエンドの開発プロセスでは、関連するポップアップ ビジネス ポップアップが多すぎます。そのうち、「XX データの追加」、「XX データの編集」、「XX 詳細データの表示」などのポップアップ ウィンドウの種類が最も一般的です。 [関連する推奨事項: vuejs ビデオ チュートリアル 、Web フロントエンド開発 ]
これらのポップアップ コンポーネントのコードの多くは、コンポーネントのステータスなど同じです。およびフォーム コンポーネント関連のメソッド...
そこで、Dialog
コンポーネントと フック
を単純に再カプセル化し、繰り返されるコードをいくつか削減しました
通常のポップアップウィンドウに使用する場合は、el-dialog
コンポーネントを直接使用するだけで十分です
しかし、私はまだまずは公式の dialog
ドキュメントを見て、どのような機能を追加できるのかを確認してみます。
...
大まかに見てから、次のようにする予定です。関数をカプセル化
Loading
Effectカプセル化する関数を決定したら、まず単純な dialog
コンポーネントを作成しましょう。
双方向バインディングを処理して、外部が v-model
を通じてポップアップ ウィンドウを直接制御できるようにします。
<template> <el-dialog :model-value="props.modelValue"></el-dialog> </template> <script setup> interface PropsType { modelValue?: boolean; } const props = withDefaults(defineProps<PropsType>(), { modelValue: false, }); const emits = defineEmits<{ (e: "update:modelValue"): void; }>(); </script>
ここではアイコン ライブラリ @element-plus/icons-vue が使用されます
インストールされていない場合は、 npm install @element-plus/icons-vueを実行します。
el-dialog
によって提供されるheader
スロットを使用して、全画面チャートを配置して閉じます。アイコンを右上隅に移動します。 show-close
属性を el-dialog
に渡して、デフォルトのアイコンを閉じます。
<template> <el-dialog :model-value="props.modelValue" :show-close="false"> <template #header> <div> <span>{{ props.title }}</span> </div> <div> <el-icon><FullScreen /></el-icon> <el-icon><Close /></el-icon> </div> </template> </el-dialog> </template> <script setup> import { FullScreen, Close } from "@element-plus/icons-vue"; </script> <style scoped> // 处理样式 :deep(.el-dialog__header) { border-bottom: 1px solid #eee; display: flex; padding: 12px 16px; align-items: center; justify-content: space-between; margin: 0; } .dialog-title { line-height: 24px; font-size: 18px; color: #303133; } .btns { display: flex; align-items: center; i { margin-right: 8px; font-size: 16px; cursor: pointer; } i:last-child { margin-right: 0; } } </style>
ポップアップ ウィンドウのタイトル テキストの内容は props
を介して渡され、デフォルトは空 (''
)
<script lang="ts" setup> interface PropsType { // 忽略之前的代码 title?: string; } const props = withDefaults(defineProps<PropsType>(), { title: "", }); </script>
現在のヘッダーを見てみましょう。 効果 (ここにはタイトルは渡されません、デフォルトは ''
です)
このボタンには、スタイル効果、対応する関数はまだ書かれていません~
対応するイベントと命令を最初にバインドします
<template> <el-dialog :model-value="props.modelValue" :show-close="false" :fullscreen="attrs?.fullscreen ?? isFullscreen" > <template #header> <div> <span class="dialog-title">{{ props.title }}</span> </div> <div class="btns"> <el-icon v-if="isFullScreenBtn" @click="handleFullscreen" ><FullScreen /></el-icon> <el-icon @click="handleClose"><Close /></el-icon> </div> </template> </el-dialog> </template> <script setup lang="ts"> import { FullScreen, Close } from "@element-plus/icons-vue"; interface PropsType { title?: string; modelValue?: boolean; hiddenFullBtn?: boolean; } const props = withDefaults(defineProps<PropsType>(), { title: "", modelValue: false, hiddenFullBtn: false, }); const emits = defineEmits<{ (e: "update:modelValue"): void; (e: "close"): void; }>(); // 当前是否处于全屏状态 const isFullscreen = ref(false); // 是否显示全屏效果图标 const isFullScreenBtn = computed(() => { if (props.hiddenFullBtn) return false; if (attrs?.fullscreen) return false; return true; }); // 开启、关闭全屏效果 const handleFullscreen = () => { if (attrs?.fullscreen) return; isFullscreen.value = !isFullscreen.value; }; // 关闭弹窗时向外部发送close事件 const handleClose = () => { emits("close"); }; </script>
次に、全画面アイコンをクリックして効果を確認します
NICE ヘッダー機能が完成しました
次に、一番下のコンテンツを処理してみましょう。デフォルトでは、「OK」と「OK」の 2 つのボタンが用意されています。 「Close」。この名前は、props
プロパティの変更に渡すこともできます。
2 つのボタンはクリック イベントにバインドされており、さまざまなイベントを外部に送信します。
<template> <div class=""> <el-dialog v-bind="attrs" :model-value="props.modelValue" :show-close="false" :fullscreen="attrs?.fullscreen ?? isFullscreen" > <template #footer> <!-- 如果没有提供其他footer插槽,就使用默认的 --> <span v-if="!slots.footer" class="dialog-footer"> <el-button type="primary" @click="handleConfirm">{{ props.confirmText }}</el-button> <el-button @click="handleClose">{{ props.cancelText }}</el-button> </span> <!-- 使用传入进来的插槽 --> <slot v-else name="footer"></slot> </template> </el-dialog> </div> </template> <script setup lang="ts"> import { useSlots } from "vue"; // 获取插槽 const slots = useSlots(); interface PropsType { title?: string; width?: string | number; isDraggable?: boolean; modelValue?: boolean; hiddenFullBtn?: boolean; confirmText?: string; cancelText?: string; } const props = withDefaults(defineProps<PropsType>(), { title: "", isDraggable: false, modelValue: false, hiddenFullBtn: false, confirmText: "确认", cancelText: "关闭", }); const handleClose = () => { emits("close"); }; const handleConfirm = () => { emits("confirm"); }; </script>
一部終了しました。コンテンツのみが残っています~
ポップアップ コンテンツが通過しますデフォルトのスロットを渡し、v-loading
タグを外側の div
要素に追加して、読み込み状態を実現します。
ポップアップ ウィンドウ全体でローディング効果を実現したい場合は、v-loading を最も外側の要素に移動してください。 el-dialog 要素上に置くことはできません。それ以外の場合は実装できないことに注意してください。 el-dialog がテレポート コンポーネントを使用しているため、v-loading が正しく動作しない可能性があります。 時間があれば勉強してみます~
<template> <div class=""> <el-dialog v-bind="attrs" :model-value="props.modelValue" :show-close="false" :fullscreen="attrs?.fullscreen ?? isFullscreen" > <div class="content" v-loading="props.loading"> <slot></slot> </div> </el-dialog> </div> </template> <script lang="ts" setup> interface PropsType { loading?: boolean; } const props = withDefaults(defineProps<PropsType>(), { loading: false, }); </script>
真ん中のloading
エフェクトを確認してみてください
el-dialog
コンポーネントは、ユーザーが選択できる多くの props
プロパティを提供しますが、 のごく一部にすぎません。ここでカプセル化するダイアログ
コンポーネントは、props
プロパティを使用します。他の props
プロパティを使用したい場合、ユーザーは何をすべきでしょうか?
たとえば、width 属性を使用する場合、カプセル化するコンポーネントで props.width
を受け取り、それを <el-dialog :width="に渡す必要がありますか?"></el-dialog>
Component?
いいえ、いいえ、いいえ、別の方法があります。完全な実行を行うときに使用した useAttrs
補助関数を覚えていますか?画面操作?
現在のコンポーネントによって渡されたプロパティを取得できます。このメソッドを使用すると、外部から渡された関数を連携して el-dialog
コンポーネント
<el-dialog v-bind="attrs" :model-value="props.modelValue" :show-close="false" :fullscreen="attrs?.fullscreen ?? isFullscreen" :before-close="handleClose" > <!-- 忽略其他代码 --> </el-dialog>
内部的に渡された props が上書きされるのを避けるために渡すことができます。削除されました。
v-bind="attrs"
は先頭に配置する必要があります。
使用する場合、関数は before-close
に渡される可能性があります。属性ですが、後で内部の handleClose
メソッドによって上書きされました。
解决方案是在handleClose
函数中,获取attrs.['before-close']
属性,如果类型是函数函数,先执行它。
const handleClose = () => { if ( Reflect.has(attrs, "before-close") && typeof attrs["before-close"] === "function" ) { attrs["before-close"](); } emits("close"); };
有关于el-dialog
组件的封装就到这里了
利用Vue composition Api
再封装一下在使用el-dialog
组件状态的管理hook
简单处理显示和加载态开关的hook
import { ref } from "vue"; export default function useDialog() { const visible = ref(false); const loading = ref(false); const openDialog = () => (visible.value = true); const closeDialog = () => (visible.value = false); const openLoading = () => (loading.value = true); const closeLoading = () => (loading.value = false); return { visible, loading, openDialog, closeDialog, openLoading, closeLoading, }; }
<template> <el-button @click="openDialog1">普通弹窗</el-button> <DialogCmp title="DialogCmp1" :hiddenFullBtn="true" v-model="visible1" @confirm="handleConfirm" @close="handleClose" > <h3>DialogCmp1</h3> </DialogCmp> </template> <script setup lang="ts"> import useDialog from "./components/useDialog"; import DialogCmp from "./components/Dialog.vue"; const { visible: visible1, openDialog: openDialog1, closeDialog: closeDialog1, } = useDialog(); </script>
针对开发管理后台弹窗状态封装的一个hook
,搭配下面的useDialogWithForm
使用。
export enum MODE { ADD, EDIT, }
import { ref } from "vue"; import { MODE } from "./types"; export default function useDialogState() { const mode = ref<MODE>(MODE.ADD); const visible = ref(false); const updateMode = (target: MODE) => { mode.value = target; }; return { mode, visible, updateMode }; }
针对表单弹窗组件封装的hooks
,接收一个formRef
实例,负责控制弹窗内标题及清空表单中的校验结果,减少多余的代码 ~
import { FormInstance } from "element-plus"; import { Ref, ref } from "vue"; import { MODE } from "./types"; import useDialogState from "./useDialogState"; export default function useDialogFn( formInstance: Ref<FormInstance> ) { const { visible, mode, updateMode } = useDialogState(); const closeDialog = () => { formInstance.value.resetFields(); visible.value = false; }; const openDialog = (target: MODE) => { updateMode(target); visible.value = true; }; return { visible, mode, openDialog, closeDialog }; }
<template> <Dialog :before-close="customClose" @confirm="confirm" v-model="visible" :title="mode == MODE.ADD ? '添加数据' : '编辑信息'" :confirm-text="mode == MODE.ADD ? '添加' : '修改'" > <el-form label-width="100px" :model="formData" ref="formDataRef" style="max-width: 460px" :rules="rules" > <el-form-item label="姓名" prop="name"> <el-input v-model="formData.name" /> </el-form-item> <el-form-item label="年龄" prop="age"> <el-input v-model="formData.age" /> </el-form-item> <el-form-item label="手机号码" prop="mobile"> <el-input v-model="formData.mobile" /> </el-form-item> </el-form> </Dialog> </template> <script setup> import { ElMessage, FormInstance } from "element-plus"; import { Ref, ref } from "vue"; import Dialog from "./Dialog.vue"; import { MODE } from "./types"; import useDialogWithForm from "./useDialogWithForm"; const rules = { name: { type: "string", required: true, pattern: /^[a-z]+$/, trigger: "change", message: "只能是英文名称哦", transform(value: string) { return value.trim(); }, }, age: { type: "string", required: true, pattern: /^[0-9]+$/, trigger: "change", message: "年龄只能是数字哦", transform(value: string) { return value.trim(); }, }, mobile: { type: "string", required: true, pattern: /^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[189]))\d{8}$/, trigger: "change", message: "请输入正确的手机号码", transform(value: string) { return value.trim(); }, }, }; interface FromDataType { name: string; age: string; mobile: string; } const formDataRef = ref<FormInstance | null>(null); let formData = ref<FromDataType>({ name: "", age: "", mobile: "", }); const { visible, closeDialog, openDialog, mode } = useDialogWithForm( formDataRef as Ref<FormInstance> ); const confirm = () => { if (!formDataRef.value) return; formDataRef.value.validate((valid) => { if (valid) { console.log("confirm"); ElMessage({ message: "提交成功", type: "success", }); closeDialog(); } }); }; const customClose = () => { ElMessage({ message: "取消提交", type: "info", }); closeDialog(); }; defineExpose({ closeDialog, openDialog, }); </script> <style scoped></style>
如果您觉得本文对您有帮助,请帮帮忙点个star
您的反馈 是我更新的动力!
以上がVue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。