Technologiefreigabe |. Wie verwende ich Hook, um el-dialog in die Popup-Fensterentwicklung einzubinden?

Popup-Fenster sind eine häufige Anforderung in der Frontend-Entwicklung. Die Komponenten im Element UI-Framework el-dialogstellen grundlegende Funktionen im Zusammenhang mit Popup-Fenstern bereit. In der tatsächlichen Entwicklung werden wir jedoch zwangsläufig auf einige benutzerdefinierte Anforderungen stoßen, z. B. auf die sekundäre Kapselung von Popup-Fenstern, um Stile und Verhaltensweisen im Projekt einheitlich zu verwalten.

In diesem Artikel erfahren Sie, wie Sie mithilfe useDialogder Hook-Kapselung el-dialogeine flexiblere und benutzerfreundlichere Popup-Komponente erhalten.

1. Klärung von Problemen

„Eine gemeinsame Komponente auf mehrere Seiten anwenden“ ist ein sehr häufiges praktisches Szenario.

Nehmen wir als Beispiel den Kauf einer Anwendung. Der Benutzer kann auf der Zahlungsseite einen Kauf tätigen oder beim Durchsuchen anderer Seiten eine Kaufanfrage auslösen um das Kaufverhalten zu vervollständigen.

Um diese Funktionalität zu erreichen, wurden in der Vergangenheit typischerweise die folgenden Schritte unternommen:

  1. Kapseln Sie die Kaufkomponente : Erstellen Sie zunächst eine allgemeine Kaufkomponente, damit diese auf verschiedenen Seiten und in verschiedenen Szenarien wiederverwendet werden kann.
  2. Rendern Sie die Kaufkomponente auf der Zahlungsseite : Betten Sie die Kaufkomponente direkt in die Zahlungsseite ein.
  3. Verwenden Sie el-dialogdie Anzeigekaufkomponenteel-dialog auf anderen Seiten: Steuern Sie die Anzeige der Komponente auf anderen Seiten und verwenden Sie visibleStatusvariablen (normalerweise eine refreaktionsfähige Variable), um das Popup und Schließen des Dialogfelds dynamisch zu steuern.

Obwohl diese Methode funktionale Anforderungen erfüllen kann, wird die Wartung komplexer und umständlicher, da die Komponente von immer mehr Seiten und Funktionen verwendet wird – für jede zusätzliche Seite, die verwendet wird, muss die Logik zur Steuerung des Anzeigens/Ausblendens wiederholt Code geschrieben werden.

Gibt es also eine bessere Möglichkeit, diesen Prozess zu vereinfachen? Ist es möglich, eine separate Funktion zu verwenden, um das Öffnen und Schließen der Kaufkomponente auf irgendeine Weise global zu steuern und dadurch Codeduplizierung und Wartungskosten zu reduzieren?

2. Über useDialog Hook

In Vue ermöglichen Hooks das „Einbinden“ von Vue-Funktionen in Funktionskomponenten oder APIs. Sie werden normalerweise in der Composition API verwendet, einer Reihe reaktionsfähiger und wiederverwendbarer Logikfunktionen, die von Vue bereitgestellt werden.

Der in diesem Artikel erwähnte Hook useDialogist ein benutzerdefinierter Hook, der el-dialogdie Grundfunktionen der Komponente kapselt. Er kann auch zusätzliche Funktionen zum Verwalten und Anzeigen von Popup-Fenstern im Projekt bereitstellen.

3. Implementieren Sie den useDialog-Hook

useDialogHook muss folgende Ziele erreichen:

  1. Erfüllen Sie die grundlegende Verwendung, el-dialogdie übergebenen grundlegenden Attribute und den angezeigten Inhalt durch den Standard-Slot, den Export openDialogund closeDialogdie Funktionen;
  2. Unterstützte el-dialogEreigniskonfiguration;
  3. Unterstützt slotdie Standardkonfiguration von Komponentenattributen;
  4. Unterstützt el-dialogandere Steckplatzkonfigurationen wie headerusw footer.;
  5. Das Auslösen bestimmter Ereignisse in Inhaltskomponenten unterstützt das Schließen des Dialogs.
  6. Unterstützte Anzeigeinhalte sind jsx, 普通文本, Vue Component;
  7. Unterstützt Rückruffunktionen, die beispielsweise steuern, ob der angezeigte Inhalt geschlossen werden kann beforeClose;
  8. Unterstützt beispielsweise die Anzeige vor Hooks onBeforeOpen;
  9. Unterstützt das Ändern von Konfigurationseigenschaften während der Definition und beim Popup;
  10. Unterstützt das Erben des Root-Vue-Prototyps. Sie können Funktionen vue-i18nwie ;$t
  11. Support- tsParameter-Eingabeaufforderung;

(1) Bereiten Sie useDialog.tsdie Definition des Dateiimplementierungstyps vor

import type { Ref } from 'vue'
import { h, render } from 'vue'
import { ElDialog } from 'element-plus'
import type {
  ComponentInternalInstance,
} from '@vue/runtime-core'

type Content = Parameters<typeof h>[0] | string | JSX.Element
// 使用 InstanceType 获取 ElDialog 组件实例的类型
type ElDialogInstance = InstanceType<typeof ElDialog>

// 从组件实例中提取 Props 类型
type DialogProps = ElDialogInstance['$props'] & {
}
interface ElDialogSlots {
  header?: (...args: any[]) => Content
  footer?: (...args: any[]) => Content
}
interface Options<P> {
  dialogProps?: DialogProps
  dialogSlots?: ElDialogSlots
  contentProps?: P
}

(2) Implementieren Sie gewöhnliche useDialogFunktionen

Die folgenden Funktionen implementieren die grundlegende Verwendung, einschließlich der Ziele 1, 2, 3, 4, 6 und 11.

el-dialogZiel 1: Grundlegende Nutzung erfüllen, grundlegende Attribute und Standard-Slot-Anzeigeinhalte, Export openDialogund closeDialogFunktionen übergeben ; Ziel 3: Attributkonfiguration von Standardkomponenten unterstützen , wie
z . B. und usw.; Ziel 6: Anzeigeinhalt von unterstützen ; Ziel 11 : Parameteraufforderungen unterstützen;el-dialog
slot
el-dialogheaderfooter
jsx普通文本Vue Component
ts

export function useDialog<P = any>(content: Content, options?: Ref<Options<P>> | Options<P>) {
  let dialogInstance: ComponentInternalInstance | null = null
  let fragment: Element | null = null

  // 关闭并卸载组件
  const closeAfter = () => {
    if (fragment) {
      render(null, fragment as unknown as Element) // 卸载组件
      fragment.textContent = '' // 清空文档片段
      fragment = null
    }
    dialogInstance = null
  }
  function closeDialog() {
    if (dialogInstance)
      dialogInstance.props.modelValue = false
  }

  // 创建并挂载组件
  function openDialog() {
    if (dialogInstance) {
      closeDialog()
      closeAfter()
    }
    
    const { dialogProps, contentProps } = options
    fragment = document.createDocumentFragment() as unknown as Element

    const vNode = h(ElDialog, {
      ...dialogProps,
      modelValue: true,
      onClosed: () => {
        dialogProps?.onClosed?.()
        closeAfter()
      },
    }, {
      default: () => [typeof content === 'string'
        ? content
        : h(content as any, {
          ...contentProps,
        })],
      ...options.dialogSlots,
    })
    render(vNode, fragment)
    dialogInstance = vNode.component
    document.body.appendChild(fragment)
  }

  onUnmounted(() => {
    closeDialog()
  })

  return { openDialog, closeDialog }
}

(3) Ziel 5 erreichen

Ziel 5: Spezifische Ereignisse in Inhaltskomponenten werfen, um das Schließen des Dialogs zu unterstützen;

  1. in der Definition unterstützt closeEventName;
interface Options<P> {
  // ...
  closeEventName?: string // 新增的属性
}
  1. Ändern Sie useDialogdie Funktion, um closeEventNamedas Ereignis zum Schließen des Dialogs zu empfangen.
export function useDialog<P = any>(content: Content, options?: Ref<Options<P>> | Options<P>) {
  // ...
  // 创建并挂载组件
  function openDialog() {
    // ...
    fragment = document.createDocumentFragment() as unknown as Element
    // 转换closeEventName事件
    const closeEventName = `on${upperFirst(_options?.closeEventName || 'closeDialog')}`

    const vNode = h(ElDialog, {
      // ...
    }, {
      default: () => [typeof content === 'string'
        ? content
        : h(content as any, {
          ...contentProps,
          [closeEventName]: closeDialog, // 监听自定义关闭事件,并执行关闭
        })],
      ...options.dialogSlots,
    })
    render(vNode, fragment)
    dialogInstance = vNode.component
    document.body.appendChild(fragment)
  }

  onUnmounted(() => {
    closeDialog()
  })

  return { openDialog, closeDialog }
}

(4) Ziele 7 und 8 erreichen

Ziel 7: Unterstützen Sie eine Rückruffunktion, die beispielsweise steuert, ob sie im angezeigten Inhalt geschlossen werden kann beforeClose.
Ziel 8: Unterstützen Sie Hooks vor der Anzeige onBeforeOpen.

  1. Es wird in der Definition unterstützt onBeforeOpenund beforeCloseDialogstandardmäßig mit Komponentenaufrufeinstellungen an die Inhaltskomponente übergeben.
type DialogProps = ElDialogInstance['$props'] & {
  onBeforeOpen?: () => boolean | void
}
  1. Ändern Sie useDialogdie Funktion, um onBeforeOpendas Ereignis zu empfangen und weiterzuleiten beforeCloseDialog.
export function useDialog<P = any>(content: Content, options?: Ref<Options<P>> | Options<P>) {
  // ...
  // 创建并挂载组件
  function openDialog() {
    // ...
    const { dialogProps, contentProps } = options
    // 调用before钩子,如果为false则不打开
    if (dialogProps?.onBeforeOpen?.() === false) {
      return
    }
    // ...
    // 定义当前块关闭前钩子变量
    let onBeforeClose: (() => Promise<boolean | void> | boolean | void) | null

    const vNode = h(ElDialog, {
      // ...
      beforeClose: async (done) => {
        // 配置`el-dialog`的关闭回调钩子函数
        const result = await onBeforeClose?.()
        if (result === false) {
          return
        }
        done()
      },
      onClosed: () => {
        dialogProps?.onClosed?.()
        closeAfter()
        // 关闭后回收当前变量
        onBeforeClose = null
      },
    }, {
      default: () => [typeof content === 'string'
        ? content
        : h(content as any, {
          // ...
          beforeCloseDialog: (fn: (() => boolean | void)) => {
            // 把`beforeCloseDialog`传递给`content`,当组件内部使用`props.beforeCloseDialog(fn)`时,会把fn传递给`onBeforeClose`
            onBeforeClose = fn
          },
        })],
      ...options.dialogSlots,
    })
    render(vNode, fragment)
    dialogInstance = vNode.component
    document.body.appendChild(fragment)
  }

  onUnmounted(() => {
    closeDialog()
  })

  return { openDialog, closeDialog }
}

(5) Ziele 9 und 10 erreichen

Ziel 9: Unterstützen Sie das Ändern von Konfigurationseigenschaften beim Definieren und Öffnen.
Ziel 10: Unterstützen Sie das Erben des Root-Vue-Prototyps. Sie können Funktionen vue-i18nwie ;$t

// 定义工具函数,获取计算属性的option
function getOptions<P>(options?: Ref<Options<P>> | Options<P>) {
  if (!options)
    return {}
  return isRef(options) ? options.value : options
}

export function useDialog<P = any>(content: Content, options?: Ref<Options<P>> | Options<P>) {
  // ...
  // 获取当前组件实例,用于设置当前dialog的上下文,继承prototype
  const instance = getCurrentInstance()
  // 创建并挂载组件,新增`modifyOptions`参数
  function openDialog(modifyOptions?: Partial<Options<P>>) {
    // ...
    const _options = getOptions(options)
    // 如果有修改,则合并options。替换之前的options变量为 _options
    if (modifyOptions)
      merge(_options, modifyOptions)
    
    // ...

    const vNode = h(ElDialog, {
      // ...
    }, {
      // ...
    })
    // 设置当前的上下文为使用者的上下文
    vNode.appContext = instance?.appContext || null
    render(vNode, fragment)
    dialogInstance = vNode.component
    document.body.appendChild(fragment)
  }

  onUnmounted(() => {
    closeDialog()
  })

  return { openDialog, closeDialog }
}

Nachdem Sie Hook über das obige Paket verwendet haben useDialog, müssen Sie beim Öffnen eines Fensters nur Hook einführen und openDialogdie Methode aufrufen, was sehr praktisch und prägnant ist. Darüber hinaus erleichtert eine solche Kapselung die spätere Änderung der Popup-Fensterlogik. Sie müssen useDialogsie nur in Hook ändern, ohne sie einzeln bearbeiten zu müssen.

4. Verwenden Sie die Dialog-Hook-Fallpraxis

Als nächstes verwenden wir useDialogHook, um das eingangs erwähnte Anwendungskaufproblem zu lösen.

(1) components/buy.vueEinkaufskomponente erstellen

<script lang="ts" setup>
  const props = defineProps({
    from: {
      type: String,
      default: '',
    },
  })
</script>
<template>
  我是购买组件
</template>

(2) pages/subscription.vueVerwenden Sie buy.vuedie Kaufkomponente auf der Seite

<script lang="ts" setup>
  import Buy from '@/components/buy.vue'
</script>
<template>

  <Buy from="subscription" />

</template>

buy.vue(3) Popup- Kaufkomponenten auf anderen Funktionsseiten

<script lang="ts" setup>
  import { useDialog } from '@/hooks/useDialog'
  const Buy = defineAsyncComponent(() => import('@/components/buy.vue'))

  const { openDialog } = useDialog(Buy, {
    dialogProps: {
      // ...
      title: '购买'
    },
    contentProps: {
      from: 'function',
    },
  })
  
  const onSomeClick = () => {
    openDialog()
  }
</script>

Erweiterung: andere Anwendungen von useDialog Hook

beforeClose& closeEventNameBeispiel: buy.vueKomponenten kaufen

<script lang="ts" setup>
  const props = defineProps({
    from: {
      type: String,
      default: '',
    },
    beforeCloseDialog: {
      type: Function,
      default: () => true,
    },
  })
  
  const emit = defineEmits(['closeDialog'])

  props.beforeCloseDialog(() => {
    // 假如from 为 空字符串不能关闭
    if (!props.from) {
      return false
    }
    return true
  })
  
  // 关闭dialog
  const onBuySuccess = () => emit('closeDialog')
</script>
<script lang="ts" setup>
  import { useDialog } from '@/hooks/useDialog'
  const Buy = defineAsyncComponent(() => import('@/components/buy.vue'))

  const { openDialog } = useDialog(Buy, {
    dialogProps: {
      // ...
      title: '购买'
    },
    contentProps: {
      from: '',
    },
  })
  
  const onSomeClick = () => {
    openDialog()
  }
</script>

Zusammenfassen

Die Verwendung useDialogder Hook-Kapselung el-dialogkann die Front-End-Technologie interessanter und prägnanter machen. Der Autor hofft auch, dass jeder diese Kapselungsmethode ausprobieren kann, um den Front-End-Code eleganter und einfacher zu warten.

Exzellente Ingenieure sind wie exzellente Köche. Sie beherrschen exquisite Koch- und Würzkünste, um jedes Gericht köstlich zu machen!


LigaAI legt großen Wert auf die Pflege und den Aufbau der Entwicklerkultur und wird weiterhin mehr Technologieaustausch und interessante Technologiepraktiken teilen.

Wir heißen Sie herzlich willkommen, dem LigaAI-Konto zu folgen, und wir freuen uns darauf, dass Sie auf die intelligente Forschungs- und Entwicklungskollaborationsplattform der neuen Generation klicken, um sich weiter mit uns auszutauschen.

Um Entwicklern dabei zu helfen, die Segel zu setzen, freut sich LigaAI darauf, Sie auf dem gesamten Weg zu begleiten!

Ein in den 1990er Jahren geborener Programmierer hat eine Videoportierungssoftware entwickelt und in weniger als einem Jahr über 7 Millionen verdient. Das Ende war sehr bestrafend! High-School-Schüler erstellen im Rahmen einer Coming-of-Age-Zeremonie ihre eigene Open-Source-Programmiersprache – scharfe Kommentare von Internetnutzern: Der inländische Dienst Taobao (taobao.com) verließ sich aufgrund des grassierenden Betrugs auf RustDesk und stellte die inländischen Dienste ein und startete die Arbeit zur Optimierung der Webversion von Java neu 17 ist die am häufigsten verwendete Java LTS-Version. Windows 11 erreicht weiterhin einen Rückgang. Open Source Daily unterstützt die Übernahme von Open Source Rabbit R1; Electric schließt die offene Plattform Apple veröffentlicht M4-Chip Google löscht Android Universal Kernel (ACK) Unterstützung für RISC-V-Architektur Yunfeng ist von Alibaba zurückgetreten und plant, in Zukunft unabhängige Spiele auf der Windows-Plattform zu produzieren
{{o.name}}
{{m.name}}

Ich denke du magst

Origin my.oschina.net/u/5057806/blog/11091317
Empfohlen
Rangfolge