目次
なぜこのアイデアを思いついたのですか?
何をカプセル化するか
EncapsulationDialog
header
フッター
コンテンツ
対処すべき詳細がいくつか残っています
封装hooks
useDialog
useDialog Demo
DialogCmp1
useDialogState 和 useDialogWithForm
useDialogState
useDialogWithForm
useDialogWithForm Demo
仓库地址
ホームページ ウェブフロントエンド Vue.js Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

Dec 28, 2022 pm 08:55 PM
vue vue3

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

なぜこのアイデアを思いついたのですか?

管理バックエンドの開発プロセスでは、関連するポップアップ ビジネス ポップアップが多すぎます。そのうち、「XX データの追加」、「XX データの編集」、「XX 詳細データの表示」などのポップアップ ウィンドウの種類が最も一般的です。 [関連する推奨事項: vuejs ビデオ チュートリアル Web フロントエンド開発 ]

これらのポップアップ コンポーネントのコードの多くは、コンポーネントのステータスなど同じです。およびフォーム コンポーネント関連のメソッド...

そこで、Dialog コンポーネントと フック を単純に再カプセル化し、繰り返されるコードをいくつか削減しました

何をカプセル化するか

通常のポップアップウィンドウに使用する場合は、el-dialogコンポーネントを直接使用するだけで十分です

しかし、私はまだまずは公式の dialog ドキュメントを見て、どのような機能を追加できるのかを確認してみます。

...

大まかに見てから、次のようにする予定です。関数をカプセル化

  • 全画面操作ボタン(右上)を提供
  • 「確認」ボタンと「閉じる」ボタンをデフォルトで提供
  • 内部追加 LoadingEffect

EncapsulationDialog

カプセル化する関数を決定したら、まず単純な 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>
ログイン後にコピー

現在のヘッダーを見てみましょう。 効果 (ここにはタイトルは渡されません、デフォルトは '' です)

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

このボタンには、スタイル効果、対応する関数はまだ書かれていません~

対応するイベントと命令を最初にバインドします

<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>
ログイン後にコピー

次に、全画面アイコンをクリックして効果を確認します

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

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>
ログイン後にコピー

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

一部終了しました。コンテンツのみが残っています~

コンテンツ

ポップアップ コンテンツが通過しますデフォルトのスロットを渡し、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エフェクトを確認してみてください

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

対処すべき詳細がいくつか残っています

el-dialog コンポーネントは、ユーザーが選択できる多くの props プロパティを提供しますが、 のごく一部にすぎません。ここでカプセル化するダイアログコンポーネントは、propsプロパティを使用します。他の props プロパティを使用したい場合、ユーザーは何をすべきでしょうか?

たとえば、width 属性を使用する場合、カプセル化するコンポーネントで props.width を受け取り、それを <el-dialog :width= に渡す必要がありますか? "props.width" / >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.[&#39;before-close&#39;]属性,如果类型是函数函数,先执行它。

const handleClose = () => {
  if (
    Reflect.has(attrs, "before-close") &&
    typeof attrs["before-close"] === "function"
  ) {
    attrs["before-close"]();
  }
  emits("close");
};
ログイン後にコピー

有关于el-dialog组件的封装就到这里了

封装hooks

利用Vue composition Api再封装一下在使用el-dialog组件状态的管理hook

useDialog

简单处理显示和加载态开关的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,
  };
}
ログイン後にコピー

useDialog Demo

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

<template>
<el-button @click="openDialog1">普通弹窗</el-button>
<DialogCmp
  title="DialogCmp1"
  :hiddenFullBtn="true"
  v-model="visible1"
  @confirm="handleConfirm"
  @close="handleClose"
>
  <h3 id="DialogCmp">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>
ログイン後にコピー

useDialogState 和 useDialogWithForm

useDialogState

针对开发管理后台弹窗状态封装的一个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 };
}
ログイン後にコピー

useDialogWithForm

针对表单弹窗组件封装的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 };
}
ログイン後にコピー

useDialogWithForm Demo

Vue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょう

<template>
  <Dialog
    :before-close="customClose"
    @confirm="confirm"
    v-model="visible"
    :title="mode == MODE.ADD ? &#39;添加数据&#39; : &#39;编辑信息&#39;"
    :confirm-text="mode == MODE.ADD ? &#39;添加&#39; : &#39;修改&#39;"
  >
    <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>
ログイン後にコピー

仓库地址

useDialog

在线demo地址

7 (1).gif

如果您觉得本文对您有帮助,请帮帮忙点个star

您的反馈 是我更新的动力!

(学习视频分享:vuejs入门教程编程基础视频

以上がVue3+フックを使用してポップアップ コンポーネントをより高速かつ効率的に作成する方法について話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

VueでBootstrapの使用方法 VueでBootstrapの使用方法 Apr 07, 2025 pm 11:33 PM

vue.jsでBootstrapを使用すると、5つのステップに分かれています。ブートストラップをインストールします。 main.jsにブートストラップをインポートしますブートストラップコンポーネントをテンプレートで直接使用します。オプション:カスタムスタイル。オプション:プラグインを使用します。

VUEのボタンに関数を追加する方法 VUEのボタンに関数を追加する方法 Apr 08, 2025 am 08:51 AM

HTMLテンプレートのボタンをメソッドにバインドすることにより、VUEボタンに関数を追加できます。 VUEインスタンスでメソッドを定義し、関数ロジックを書き込みます。

vue.jsでJSファイルを参照する方法 vue.jsでJSファイルを参照する方法 Apr 07, 2025 pm 11:27 PM

vue.jsでJSファイルを参照するには3つの方法があります。タグ;; mounted()ライフサイクルフックを使用した動的インポート。 Vuex State Management Libraryを介してインポートします。

VueでWatchの使用方法 VueでWatchの使用方法 Apr 07, 2025 pm 11:36 PM

Vue.jsの監視オプションにより、開発者は特定のデータの変更をリッスンできます。データが変更されたら、Watchはコールバック関数をトリガーして更新ビューまたはその他のタスクを実行します。その構成オプションには、すぐにコールバックを実行するかどうかを指定する即時と、オブジェクトまたは配列の変更を再帰的に聴くかどうかを指定するDEEPが含まれます。

Vue Multi-Page開発とはどういう意味ですか? Vue Multi-Page開発とはどういう意味ですか? Apr 07, 2025 pm 11:57 PM

VUEマルチページ開発は、VUE.JSフレームワークを使用してアプリケーションを構築する方法です。アプリケーションは別々のページに分割されます。コードメンテナンス:アプリケーションを複数のページに分割すると、コードの管理とメンテナンスが容易になります。モジュール性:各ページは、簡単に再利用および交換するための別のモジュールとして使用できます。簡単なルーティング:ページ間のナビゲーションは、単純なルーティング構成を介して管理できます。 SEOの最適化:各ページには独自のURLがあり、SEOに役立ちます。

Vueによる前のページに戻る方法 Vueによる前のページに戻る方法 Apr 07, 2025 pm 11:30 PM

vue.jsには、前のページに戻る4つの方法があります。$ router.go(-1)$ router.back()outes&lt; router-link to =&quot;/&quot; Component Window.history.back()、およびメソッド選択はシーンに依存します。

VUEトラバーサルの使用方法 VUEトラバーサルの使用方法 Apr 07, 2025 pm 11:48 PM

Vue.jsには配列とオブジェクトを通過するには3つの一般的な方法があります。V-Forディレクティブは、各要素をトラバースしてテンプレートをレンダリングするために使用されます。 V-BindディレクティブをV-Forで使用して、各要素の属性値を動的に設定できます。 .mapメソッドは、配列要素を新しい配列に変換できます。

Vueにタグをジャンプする方法 Vueにタグをジャンプする方法 Apr 08, 2025 am 09:24 AM

VUEでタグのジャンプを実装する方法には、HTMLテンプレートでAタグを使用してHREF属性を指定する方法が含まれます。 VUEルーティングのルーターリンクコンポーネントを使用します。 JavaScriptでこれを使用します。$ router.push()メソッド。パラメーターはクエリパラメーターに渡すことができ、ルートは動的ジャンプのルーターオプションで構成されています。

See all articles