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

青灯夜游
リリース: 2022-12-28 20:55:27
転載
3135 人が閲覧しました

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

header

ここではアイコン ライブラリ @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>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 サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート