🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

沒開玩笑,全框架支持的 dialog 元件,支持響應式

前言

朋友們好啊,我是 auto-plugin 掌門人德萊厄斯。

剛才有個朋友問我,德老師發生什麼事了,我說怎麼回事,給我發了幾張截圖,我一看,嗷!原來是昨天,有兩個小專案,兩三個頁面,一個只有表單收集頁,一個是登入頁加資訊頁。他們說,唉...有一個說是他不想在這種小專案中引入大型元件庫,德老師你能不能教教我 auto 功法,哎幫助開發一下,我的小專案。我說可以,我說你老用元件庫大力出奇蹟,不好用,他不服氣。我說小朋友,你一個元件庫來用在我 vue 和 react 上,他說用不了。他說你這也沒用。我說我這個有用,這是統一,傳統開發是講究一次編譯到處運行。二百個元件的大型元件庫,掰不動我這一個小元件。

啊...哈,他非和我試試,我說可以。哎...我一說啪一下就給 element-plus 引入了,很快啊!然後上來就是一個 message,一個 tooltip,一個響應式佈局。我全部防出去了啊,防出去以後,自然是傳統開發點到為止,autohue 藏在 github 沒給他看。我笑一下準備上班。由於這時間,按傳統開發的點到為止他已經輸了,如果 autohue 發力,一下就把他元件庫打散架了,放在 github 沒給他看,他也承認,說元件庫沒有這種功能。啊,我收手的時間不聊了,他突然襲擊說 dialog 你沒有,啊,我大意了啊,沒有做。哎,他的 dialog 給我臉打了一下,但是沒關係啊!他也說,啊他截圖也說了,兩分多鐘以後,當時流眼淚了,捂著眼說,我說停停。然後兩分鐘以後,哎兩分鐘以後就好了,我說小夥子你不講武德你不懂,說德老師對不起對不起,我不懂規矩。啊,他說他是亂打的,他可不是亂打啊,mmessage、tooltip 訓練有素,後來他說他練過兩年開源,啊,看来是有备而来。這兩個年輕人不講武德,來騙,來偷襲!我 26 歲的老同志,這好嗎這不好,我勸!這位年輕人耗子尾汁,好好反思,以後不要再犯這樣的聰明,小聰明啊。啊,呃...開發要以和為貴,講究統一,不要搞窩裡鬥,謝謝朋友們。

dialog 的場景往往出現在表單收集、確認詢問的場景,在 JQ 時代,我們可能很常用瀏覽器自帶的 alert,但是這東西會阻塞主進程,且樣式也不太好控制,或者用 bootstrap 的元件。到了框架時代,出現了各種元件庫,但是它們都存在幾個問題

  1. 要用必須全量安裝(雖然現在大家都支持樹搖)
  2. 修改樣式比較麻煩
  3. 不支持跨框架,感知不統一

它們跟自身生態、框架生態深度綁定,雖然大多數時候我們用起來很方便,心智負擔也很低。但是它們不可避免地出現了上述三個問題。

那麼如果像我剛才提到的,只做一兩個頁面的簡單應用,也要引入元件庫嗎?要是引入元件庫,你還要畫兩分鐘思考一下,你要用 vue 生態還是 react 生態的。那有人說了,現在元件庫也是跨框架支持啊,下載對應的包就行了,但是你看,截至目前2025年10月28日,ant-design 的 vue 版本還停留在 4.26,而 react 版本已經到了 5.27.6(BTW:antd for react 元件庫現在已支持 autofit.js)。容易發現,使用不同的框架,即使是同一個元件庫,不同框架開發體驗也是不同的。

這在多元化我們的選擇的同時,也割裂了開發的生態。

碰巧我最近又在寫一個簡單專案,需要一個 dialog 元件,我又討厭原子化 css 的寫法(這就是為什麼不直接用 shadcn 的原因),怎麼辦呢,再實現一個得了。

autodialog.js

github: github.com/Auto-Plugin…

我取名可不是瞎取的(也瞎取過),這個 autodialog.js 是真正的框架無關的 dialog 元件。那么它的 auto 體現在哪呢?它可以自動識別傳入的彈窗內容是來自什麼框架!甚至不會破壞 vue 的響應式和 react 的狀態,你甚至可以在 原生 html、svelte、solid、augular 中無縫使用,而不破壞框架本身的特性。更驚奇的是,調用方法是一模一樣的

在說實現思路之前,我想讓你先感受一下 autodialog.js 的使用

快速使用示例

原生 HTML

import autodialog from 'autodialog.js'

autodialog.show('<div>Hello World!</div>')

Vue 3

import autodialog from 'autodialog.js'
import MyDialog from './MyDialog.vue'

autodialog.show(MyDialog, {
  props: { title: '你好 Vue' }
})

React 18+

import autodialog from 'autodialog.js'
import MyDialog from './MyDialog.tsx'

autodialog.show(MyDialog, {
  props: { message: '你好 React' }
})

666 有沒有?autodialog.js 內部自動判斷了傳入的元件類型,使無論什麼框架的調用方式都完全一致!

QQ20251028-161029.webp

而且除了遮罩和最簡單的動畫外(當然也提供了自定義方式),其餘樣式完全由你的內容決定!你完全無需寫多層選擇器或者使用 !important 來覆蓋樣式。

原理解析

要實現這種跨框架,又保留框架特性,又保持感知統一的工具庫,其實有一條成熟且穩妥的道路,那就是適配器(adapter)

autodialog.js 也是這樣,它的 core 包是純 js 的,但是接受各種各樣的適配器,我定義的適配器格式如下:

/**
 * 適配器接口
 * - render: 渲染內容到 panel 上
 * - unmount: 卸載 panel 上的內容(可選)
 */
export interface Adapter {
  render: (content: any, options: { container: HTMLElement; panel: HTMLElement; [key: string]: any }) => void
  unmount?: (panel: HTMLElement) => void
}

比如要實現一個 vue 的適配器就是這樣:

import { createApp, h, type Component } from 'vue'

interface VueRenderOptions {
  panel: HTMLElement
  title?: string
  props?: Record<string, any>
  onClose?: () => void
}

export const VueAdapter = {
  render(Component: Component, { panel, title, props = {}, onClose }: VueRenderOptions) {
    // 創建一個 Vue 應用實例
    const app = createApp({
      render() {
        return h('div', { class: 'autodialog-vue-wrapper' }, [
          title ? h('div', { class: 'autodialog-header' }, title) : null,
          h(Component, { ...props, onClose }),
        ])
      },
    })

    // 挂載到 panel
    app.mount(panel)
    (panel as any)._vueApp = app
  },

  unmount(panel: HTMLElement) {
    const app = (panel as any)._vueApp
    if (app) {
      app.unmount()
      delete (panel as any)._vueApp
    }
  },
}

當然,autodialog.js 已經內置了 vue 和 react 的適配器。

如果你使用 svelte,autodialog.js 沒有內置,那麼你可以使用適配器註冊器函數來外部掛載一個適配器,像下面的章節(進階使用)裡就實現了一個 svelte 適配器。

你完全不必擔心適配器很難寫,因為你使用你熟悉的框架,如果你熟悉 vue,那麼看一下上面的 vue 適配器,它也只是使用了 vue 的 render 和 h 函數。

在 autodialog.js 的 core 中,是這樣自動判斷內容來自什麼元件,該用什麼適配器的:

/**
 * 自動檢測邏輯(detect 不強制)
 */
private detectAdapter(content: any): Adapter {
  // 1️⃣ 優先使用用戶註冊的自定義適配器
  for (const { detect, adapter } of Dialog.customAdapters) {
    try {
      // detect 可省略:省略則直接匹配
      if (!detect || detect(content)) return adapter
    } catch { }
  }

  // 2️⃣ 內置適配器兜底
  if (typeof content === 'string' || content instanceof HTMLElement || content instanceof DocumentFragment)
    return HtmlAdapter

  if (content && (typeof content === 'object' || typeof content === 'function')) {
    const proto = (content as any).prototype
    const hasSetup = !!(content as any).setup
    const hasRender = !!(content as any).render
    const isClass = proto && proto.isReactComponent
    const isFunctionComponent = typeof content === 'function' && /^[A-Z]/.test(content.name)
    if (hasSetup || hasRender) return VueAdapter
    if (isClass || isFunctionComponent) return ReactAdapter
  }

  throw new Error('[autodialog] Unsupported component type.')
}

內置的 vue 和 react 適配器是直接檢查了各自元件對象的特徵,從而實現自動拾取適配器,自定義適配器則要寫一個 detect 函數(特徵檢查),當然這不是必須的,因為你在你的框架中只會有一種元件傳入,所以不必檢查特徵,detectAdapter 函數會優先拾取你的自定義適配器。

autodialog 有一個單例的默認導出,你可以直接導入使用,也可以引入 Dialog 類,實現多例彈窗。

所謂大道至簡,核心原理就只有這些內容了!

進階使用

API

autodialog.show(content, options?)

選項 類型 默認值 說明
title string undefined 可選標題
props object {} 傳遞給元件的參數
showMask boolean true 是否顯示遮罩層
allowScroll boolean false 是否允許滾動頁面
animation boolean true 是否啟用動畫
animationDuration number 200 動畫持續時間(毫秒)
animationClass { enter?: string; leave?: string } - 自定義動畫類名
onBeforeOpen () => void - 打開前
onOpened () => void - 打開後
onBeforeClose () => void - 關閉前
onClosed () => void - 關閉後
onMaskClick () => void - 點擊遮罩層時觸發

自定義適配器(例如 Svelte)

import { Dialog } from 'autodialog.js'
import { mount } from 'svelte'

export const SvelteAdapter = {
  render(Component: any, { panel, props = {}, onClose }: any) {
    const instance = mount(Component, {
      target: panel,
      props: { ...props, onClose }
    })
    (panel as any).__svelte__ = instance
  },
  unmount(panel: HTMLElement) {
    const inst = (panel as any).__svelte__
    inst?.destroy?.()
    delete (panel as any).__svelte__
  }
}

// ✅ 註冊自定義適配器(detect 可省略)
Dialog.registerAdapter({
  name: 'svelte',
  adapter: SvelteAdapter
})

現在可以直接這樣調用:

import MyDialog from './MyDialog.svelte'
autodialog.show(MyDialog, { props: { text: '來自 Svelte 的彈窗 ✨' } })

設計理念

Autodialog 的設計遵循三個核心原則:

  1. 框架獨立:核心邏輯不依賴 Vue、React 或其他框架。
  2. 可擴展性:任何渲染系統都可以通過 Adapter 接入。
  3. 用戶主導:樣式、動畫與生命週期完全開放給用戶控制。

結語

希望 auto-plugin 的插件能給你帶來幫助,讓我們歡迎新成員:autodialog.js !

github:github.com/Auto-Plugin…

npm:www.npmjs.com/package/aut…

別忘了免費的小星星點一下。


原文出處:https://juejin.cn/post/7565876621653573659


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝28   💬4   ❤️7
840
🥈
我愛JS
📝2   💬8   ❤️2
113
🥉
御魂
💬1  
4
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付