朋友們好啊,我是 auto-plugin 掌門人德萊厄斯。
剛才有個朋友問我,德老師發生什麼事了,我說怎麼回事,給我發了幾張截圖,我一看,嗷!原來是昨天,有兩個小專案,兩三個頁面,一個只有表單收集頁,一個是登入頁加資訊頁。他們說,唉...有一個說是他不想在這種小專案中引入大型元件庫,德老師你能不能教教我 auto 功法,哎幫助開發一下,我的小專案。我說可以,我說你老用元件庫大力出奇蹟,不好用,他不服氣。我說小朋友,你一個元件庫來用在我 vue 和 react 上,他說用不了。他說你這也沒用。我說我這個有用,這是統一,傳統開發是講究一次編譯到處運行。二百個元件的大型元件庫,掰不動我這一個小元件。
啊...哈,他非和我試試,我說可以。哎...我一說啪一下就給 element-plus 引入了,很快啊!然後上來就是一個 message,一個 tooltip,一個響應式佈局。我全部防出去了啊,防出去以後,自然是傳統開發點到為止,autohue 藏在 github 沒給他看。我笑一下準備上班。由於這時間,按傳統開發的點到為止他已經輸了,如果 autohue 發力,一下就把他元件庫打散架了,放在 github 沒給他看,他也承認,說元件庫沒有這種功能。啊,我收手的時間不聊了,他突然襲擊說 dialog 你沒有,啊,我大意了啊,沒有做。哎,他的 dialog 給我臉打了一下,但是沒關係啊!他也說,啊他截圖也說了,兩分多鐘以後,當時流眼淚了,捂著眼說,我說停停。然後兩分鐘以後,哎兩分鐘以後就好了,我說小夥子你不講武德你不懂,說德老師對不起對不起,我不懂規矩。啊,他說他是亂打的,他可不是亂打啊,mmessage、tooltip 訓練有素,後來他說他練過兩年開源,啊,看来是有备而来。這兩個年輕人不講武德,來騙,來偷襲!我 26 歲的老同志,這好嗎這不好,我勸!這位年輕人耗子尾汁,好好反思,以後不要再犯這樣的聰明,小聰明啊。啊,呃...開發要以和為貴,講究統一,不要搞窩裡鬥,謝謝朋友們。
dialog 的場景往往出現在表單收集、確認詢問的場景,在 JQ 時代,我們可能很常用瀏覽器自帶的 alert,但是這東西會阻塞主進程,且樣式也不太好控制,或者用 bootstrap 的元件。到了框架時代,出現了各種元件庫,但是它們都存在幾個問題
它們跟自身生態、框架生態深度綁定,雖然大多數時候我們用起來很方便,心智負擔也很低。但是它們不可避免地出現了上述三個問題。
那麼如果像我剛才提到的,只做一兩個頁面的簡單應用,也要引入元件庫嗎?要是引入元件庫,你還要畫兩分鐘思考一下,你要用 vue 生態還是 react 生態的。那有人說了,現在元件庫也是跨框架支持啊,下載對應的包就行了,但是你看,截至目前2025年10月28日,ant-design 的 vue 版本還停留在 4.26,而 react 版本已經到了 5.27.6(BTW:antd for react 元件庫現在已支持 autofit.js)。容易發現,使用不同的框架,即使是同一個元件庫,不同框架開發體驗也是不同的。
這在多元化我們的選擇的同時,也割裂了開發的生態。
碰巧我最近又在寫一個簡單專案,需要一個 dialog 元件,我又討厭原子化 css 的寫法(這就是為什麼不直接用 shadcn 的原因),怎麼辦呢,再實現一個得了。
github: github.com/Auto-Plugin…
我取名可不是瞎取的(也瞎取過),這個 autodialog.js 是真正的框架無關的 dialog 元件。那么它的 auto 體現在哪呢?它可以自動識別傳入的彈窗內容是來自什麼框架!甚至不會破壞 vue 的響應式和 react 的狀態,你甚至可以在 原生 html、svelte、solid、augular 中無縫使用,而不破壞框架本身的特性。更驚奇的是,調用方法是一模一樣的。
在說實現思路之前,我想讓你先感受一下 autodialog.js 的使用
import autodialog from 'autodialog.js'
autodialog.show('<div>Hello World!</div>')
import autodialog from 'autodialog.js'
import MyDialog from './MyDialog.vue'
autodialog.show(MyDialog, {
props: { title: '你好 Vue' }
})
import autodialog from 'autodialog.js'
import MyDialog from './MyDialog.tsx'
autodialog.show(MyDialog, {
props: { message: '你好 React' }
})
666 有沒有?autodialog.js 內部自動判斷了傳入的元件類型,使無論什麼框架的調用方式都完全一致!
而且除了遮罩和最簡單的動畫外(當然也提供了自定義方式),其餘樣式完全由你的內容決定!你完全無需寫多層選擇器或者使用 !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 類,實現多例彈窗。
所謂大道至簡,核心原理就只有這些內容了!
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 |
- | 點擊遮罩層時觸發 |
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 的設計遵循三個核心原則:
希望 auto-plugin 的插件能給你帶來幫助,讓我們歡迎新成員:autodialog.js !
github:github.com/Auto-Plugin…
npm:www.npmjs.com/package/aut…
別忘了免費的小星星點一下。