稀土掘金技术社区

没开玩笑,全框架支持的 dialog 组件,支持响应式

(💰TRAE SOLO实战赛强势上线,速戳上图 了解详情🔍)

前言

朋友们好啊,我是 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 - Auto-Plugin/autodialog.js 我取名可不是瞎取的(也瞎取过),这个 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 内部自动判断了传入的组件类型,使无论什么框架的调用方式都完全一致!
而且除了遮罩和最简单的动画外(当然也提供了自定义方式),其余样式完全由你的内容决定!你完全无需写多层选择器或者使用 !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: Auto-Plugin/autodialog.js npm:autodialog.js - npm
图片
点 击 " 阅 读 原 文 " 了 解 详 情 ~