npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue3-command-dialog

v1.1.11

Published

## 英文文档

Downloads

228

Readme

Vue 3 通用的命令式弹窗方案

英文文档

您可以通过以下链接查看英文文档:

English Documentation

天下苦弹窗开发久矣, 作为一个前端开发, 弹窗的开发体验一直很糟糕, 尤其是嵌套弹窗, 状态管理, 销毁重建等问题, 让人不胜其烦. 所以, 我决定实现一个通用的命令式弹窗解决方案来解决这些痛点.这是一个专为 Vue 3 设计的通用命令式弹窗解决方案。它提供了一种灵活且可扩展的方式来管理和控制应用程序中的弹窗。虽然唤作弹窗,但是它不仅仅局限于弹窗,理论上任何组件都可以进行适配.

这里就不比较声明式弹窗和命令弹窗的优缺点了,如果你已经尝试探索命令弹窗而看到这里,那么你或许已经切实的体会到了声明式弹窗开发的繁琐和痛苦,那么不妨试试这个库,或许能给你带来一些不一样的体验.

特性

  • 命令式 API,弹窗开发变更为编程式,解放弹窗生产力!
  • 支持弹窗嵌套,链式管理,并提供完整上下文支持(状态管理,路由,国际化等).
  • 灵活的配置, 支持自定义属性,插槽,事件处理器等.
  • 开箱即用,已实现与 Element Plus 的 Dialog 组件以及 vant 的 Popup 组件的适配,也可以自行拓展以便更贴切你的实际业务.
  • 命令式组件核心逻辑解耦,可自行适配不同的 UI 库目标组件

安装

npm i vue3-command-dialog
# 或者
pnpm i vue3-command-dialog
# 或者
yarn add vue3-command-dialog

在线示例

您可以通过以下链接查看在线示例(以 element-plus Dialog 为例):

Example

常见问题

弹窗没有正确的显示

这种原因是对应组件的css没有被成功引入,导致页面显示异常.这很可能是你使用了按需导入或者自动导入等手段,那么如果你在使用命令弹窗之前没有使用过这些组件,那么就可能会出现类似情况.一般建议手动在 main 的地方导入一下,比如下面这样:

import { createApp } from "vue";
import App from "./App.vue";

// 导入vant-popup弹窗样式
import("vant/es/popup/style");
// 导入element-plus dialog弹窗样式
import("element-plus/es/components/dialog/style/css");

const app = createApp(App);
app.mount("#app");

如果你对打包体积等没有要求,更简单粗暴的方法是直接全量导入组件库的 CSS:

// 导入vant全量样式
import "vant/lib/index.css";

// 导入element-plus全量样式
import "element-plus/dist/index.css";

你如何适配自己 UI 库组件

除了已经适配的 Element Plus 的 Dialog 组件以及 vant 的 Popup 组件, 您也可以自行适配您自己的 UI 库组件, 具体可以参考以下步骤:

具体可以借鉴示例代码中对 element-plus 以及 vantui 的实现,这里只说一下核心逻辑;

1.我们需要 CommandProvider 函数来对我们的目标组件进行包装, 它的最主要的作用是对被包裹的组件注入Consumer对象,那么我们的弹窗内部组件就可以接收到这个对象,它是我们对弹窗进行控制的主要手段.这个对象上有下列属性和方法:

/** 弹窗消费者对象,或者也可理解为弹窗实例实例~ */
export interface IConsumer {
  /** 弹窗promise */
  promise: Promise<any>;
  /** 弹窗promise执行器参数resolve */
  resolve: (val?: any) => void;
  /** 弹窗promise执行器参数reject */
  reject: (reason?: any) => void;
  /** 弹窗销毁,并解决promise */
  destroyWithResolve: (val?: any) => void;
  /** 弹窗销毁,并拒绝promise */
  destroyWithReject: (reason?: any) => void;
  /** 弹窗销毁,但是不继续推进promise的状态改变 */
  destroy: (external?: boolean) => void;
  /** 弹窗是否可见响应式变量,虽然已经提供了hide以及show方法不需要通过该属性来控制弹窗的显示与隐藏,但是为了方便一些特殊场景,还是提供了该属性,比如你需要watch这个属性来做一些事情 */
  visible: Ref<boolean>;
  /** 隐藏 */
  hide: () => void;
  /** 显示 */
  show: () => void;
  /** 订阅取消 */
  off: (name: string | symbol, callback: Function) => void;
  /** 订阅 */
  on: (name: string | symbol, callback: Function) => void;
  /** 单次订阅 */
  once: (name: string | symbol, callback: Function) => void;
  /** 发布 */
  emit: (name: string | symbol, ...args: any) => void;
  /** 一般建议赋值为UI库的弹窗实例实例Ref */
  componentRef?: Ref<any> | undefined;
  /** 弹窗挂载的html元素 */
  container: HTMLDivElement;
  /** 弹窗嵌套堆栈 */
  stack: IConsumer[];
  /** 当前在弹窗嵌套堆栈中的索引 */
  stackIndex: number;
}

你不用关心这个对象的创建销毁等逻辑,只需要知道有这么一个对象,以及它身上有哪些属性和方法即可.你还可以注意到,这个对象上还有on once emit off等方法,通过这些 api 注册的事件函数都会严格限制在 consumer 对象下,所以不同的consumer对象的事件注册发布均不互相影响;同时你也不用关心事件的解绑等逻辑,这些内部已经帮你处理好了.

CommandProvider 同时也会返回一个consumer对象,以供弹窗外部使用,弹窗内部和外部拿到的 consumer 是同一个对象,所以他们是全等(===)的.

弹窗内部组件获取 consumer 对象的方式为调用getConsumer, 该函数会返回一个 consumer 对象,它一样只能在 setup 顶部直接调用,不可条件调用或者异步调用.

2.剩余的就是CommandProvider函数参数的介绍了,

parentInstance: ComponentInternalInstance | null,
uiComponentVnode: Component,
config: {
  // 你大可直接使用provide注入,内部一样能接收到,但是你想实现更私有的作用域,可以将需要注入的数据放置在这个对象下
  provideProps?: Record<string, any>;
  // 挂载点,默认body
  appendTo?: string | HTMLElement;
  // 内部维护的响应式变量,你需要完整的将其传递进去,不要将响应式变量解包
  visible: Ref<boolean>;
}

其余并不复杂,更多查看 element-plus 适配代码:/src/components/ElementPlusDialog.tsx

一些建议

  • 强烈建议你的项目配置 jsx!如果你能忍受一味的使用h函数,那么你可以忽略这个建议.

  • 尽管 consumer 对象实现了一个订阅模式,但是你应该避免通过它来进行内部和外部的通信,它的出现是为了实现对命令弹窗的组件的增强,不建议用于业务开发.所以,情非得已之下,请尽量使用destroyWithRejectdestroyWithResolve来借助 promise 的特性进行数据交互.当然,也可以使用很常规的propsemit等手段进行通信.