前端基于 Vue3 + Element Plus + Vite + TypeScript 。当前,每个页面都有类似于:详情对话框、编辑对话框和各种嵌套对话框等,该复用的已经复用过了,剩下的对话框都含有各自的特色内容,不宜复用,强行复用可能会导致单文件代码量过多不利于后期维护。
目前的疑问:我现在的做法是拆分每个 el-dialog 为单文件组件,存放到 components 文件夹下(可能有违 components 这个文件夹的定义,我的理解是 components 用来存放一些复用性很高的组件,而不是这种零散的对话框单组件文件)所以就导致我现在的项目结构很多 xxxDialog.vue 文件,既不知道如何归类存放,也不知道该怎么高效复用。

组件只用于某一个页面,不需要再进行封装了。

收藏了,想看看大家的做法。

那存放这些页面对话框,只能放到 components 文件夹下?我想用更优雅一点的方式,不然太多 xxxDialog.vue 文件了,看着碍眼

不行。这样 template 太长了,不好维护

确实是,有些对话框的内容都好长了,全放在一个页面 template 下的话,动不动就上千行代码,确实很不利于后面维护

没办法。我们有三十多个 xxModal.vue, 一个 modal 都至少一百五十行代码, 不可以把他放在原来的页面,不然不好维护

components 文件夹下是放公共组件的地方,不能公用的组件不要往那放。比如商品浏览这个功能的所有组件,应该是在 src/views/GoodsList/目录下这个目录下应该有/GoodsItem.vue GoodsDetail.vue GoodsDialog.vue 而不是把 GoodsDialog 放到/src/components/下去

#6 难道只能这样滋生更多的 xxxModel.vue 或者 xxxDialog.vue 吗?太多类似文件感觉没充分复用,但强行复用又是另一个大麻烦

还有就是你没有必要为了抽象而抽象,为了提取组件而提取组件。提不提去组件不是按代码行数来的,同样几百行代码,有时候提出去方便,有时候写在一起方便。

有想过这个问题,谢谢,后面如果实在没有更好的办法,我就把对话框组件文件挪到 views 页面同级目录下,毕竟 components 只适合放通用性组件文件

我的习惯是,基础组件以及业务类组件,放在 components 目录下。 基础组件按照功能进行文件夹划分 业务组件按照业务类型进行文件夹划分 如果业务中出现了强依赖的组件,直接扔到该业务页面下建立一个文件夹进行管理。其实这种情况非常普遍,有一些业务功能看似能复用,但是他往往带有非常强的业务特有场景,这个时候你在想去复用他其实是没有意义的。

在工作中也看到一些同事,为了代码的复用,拆成 N 个组件,然后组件中甚至暴露了 N 多个 slot ,这样反而本末倒置,为了抽象而抽象。

#11 那确实没得好的办法了,官方对 components 文件夹的定义是可复用性组件,对 views 文件夹的定义是可通过浏览器访问的页面( vue-router 下引入的组件文件来自 views 目录),所以如果没有好的归类地方,那最好的应该还是随 views 一起存放,外层用文件夹包裹以区分不同的页面内容

#12 也不是为了代码复用而拆分,而是页面内容太多不利于维护,拆分后可以做到一个页面只完成一项或多项类似的功能,全部糅杂在一个页面真的很头疼,尤其是对话框很多的情况下

组件单独抽象一方面是复用,一方面是解耦,把对话框单独挪出去可以保证对话框内的逻辑独立,然后在主页面引用多个独立的对话框组件,是挺好的实践

正确的

components 下面是公共组件,页面目录放一个 components 存放页面组件

这样?./index.vue./components/./dialogs/./list/index.vue./list/dialogs/./detail/index.vue./detail/dialogs/

整不是应该抽离出来,写成 Promise 的方式调用吗?

从某种程度上讲,dialog 也是一个 route 。。。

有了解过,但没深入了解,应该可行

本质应该算是后端的非主流半吊子前端来说下:我会习惯按业务逻辑相关性来放, 不通用 但 一起完成某个相关业务逻辑的放一个目录下如:..../(some biz)/..../(some biz)/list.vue..../(some biz)/detail.vue..../(some biz)/edit.vue..../(some biz)/useHost.ts..../(some biz)/popupList.vue..../(some biz)/mergeToolDialog.vue..../(some biz)/useBizLogic.ts -- ( 只是举个例子,一般还是会叫具体完成的事)

看过多个开源的前端项目,都是这样的,最主要的页面取名 Index.vue ,剩下的如详情对话框,就取名 detail.vue ,编辑对话框就取名 edit.vue

不需要考虑那么多复用的情况,components 文件夹你只需要放公用的组件就好了,比如你在 Element Plus 的 Dialog 组件二次封装了组件。其它的页面内业务 Dialog 直接放在对应路由页面下的 components 文件夹或者不需要文件夹,直接 xxDialog.vue 或 xxModel.vue 就好了。

components 放业务无关的拆出去的组件,containers 放包含各种业务逻辑的拆出去的组件

1 、约束文件夹、文件的路径和命名规则2 、公共的 hooks 、utils 、组件的文档编写3 、异步引入减少加载时常和页面体积4 、测试(可选)只要规范这三个,不管是什么前端项目基本都清晰可维护了

对话框内容不多的话可以改成命令式调用的方法,传几个参数改改里面的字就够了。类似于 element 里的 this.$message 。如果各有业务内容的话那分成多个组件也是没有办法的事,不用内疚。业务特有的组件的话,我们公司的习惯是 views 下分页面,比如 foo 页面,如果 foo 页面下有专有组件就在 foo 文件夹下再建一个 components 文件夹,结构大概是这样views/ foo/ components/ BarDialog.vue index.vue

#21 不知道其他人是怎么解决的,我前几个月也尝试看看能不能将 dialog 改造成类似 model 一样的命令式调用,以减少页面上那一堆管理弹窗状态/传值的代码,现在用下来感觉还不错。
#28 谢谢,有时间一定得去研究一下

我每个小模块会有自己的 components 文件夹,不是公用组件的话我就丢在这里面了。

还有一种方法,写一个文件叫 xxxModal.tsx,把这个页面的所有用到的 modal 文件放里面.

你的页面 dialog 样式 功能 回调都不一样,全靠传参反而更麻烦。一个模块用一个挺好的

你最后就算封装成一个组件了,该写的还是少不了,json 配置 table form 看过吗,其实你定义的 schema 跟直接写 html 没什么区别的。

弹出层这个事情也是为难了我很久,写到最后还是命令式调用,更符合直觉,你看看我写的这个库找找灵感 vue-modal-provider.netlify.app/

过度封装带来的后果是更难的维护和组件的膨胀

谢谢,我研究一下你的代码

首先弹窗组件的复用看功能,不从 ui 来划分复用。然后是弹窗声明式编写,但是命令式调用。

我最讨厌那种页面拆分成 N 个组件的了....有想过后面接手兄弟的感受吗?得看这 N 个组件的属性传递、自定义方法等,对了,vscode 默认还不支持点击跳转到该文件,来回跳转头都大了... 真不如塞到一个页面里 。什么, 页面代码太长?那是你注释不清晰....分块集中写在一起也很好阅读

我在写完这个库之后,就把弹窗当做页面,放在用弹窗的页面文件夹里,因为弹窗里面本身就承载像表单这种业务逻辑,只是他是命令调用不是当路由跳走的页面

我是拆组件,编辑和添加用一个组件,用 type 来区分,如果详情也是弹窗,相同的部分多的话就共用。

#28 我之前也是这么写的,不过没有开源,我现在写 React 了,这是 React + antd 版的 bowencool.github.io/create-antd-modal/

一样的技术栈,但是我把 element plus 中一个 input 组件就基于业务不同,封装了几十个业务组件,select 也是,封装了几十个,然后 form 表单是基于 一个配置信息 v-for 渲染出来的,用动态组件。什么业务逻辑都在业务组件中封装好,校验什么的,核心就是只关注入参和出参,写好文档。而表单配置信息,你乐意写在一个 config.ts 里都行,或者直接写在 index.vue 同级目录下。

分情况,如果这个 dialog 的显示内容不需要特殊的布局和排版以及内容,那么可以使用命令式调用, 把 title, content ,buttons 传递进去。比如单纯的 alert, confirm 等。对于需要特殊布局,然后还有数据驱动的,甚至是一些逻辑的,才抽离成一个 vue 组件。

我认为关键在于你如何定义 dialog 的多于少。如果一个页面或者整个项目里要使用的 dialog 数量(按类型计算)会以数量级的方式增加,比如这次需求有 2 个弹窗,下次需求就可能加到 20 个或者更多,这种情况,是需要考虑如何从项目顶层设计才能更好地承载这样的业务模式;如果只是递增形式的变多,那只是业务需求而已,按照需求的描述,做好基本的代码管理就可以了。什么是好代码?不要让维护者看不懂你的代码就是好代码。

弄个 modalConfig.js 进行集中配置,自己二次封装太麻烦了

products└─ components ├─ product-edit.vue └─ product-view.vue├─ product-details.vue└─ product-list.vue


当前页面路径下开个 components 文件夹

```xxx└─ index.vue└─ components └─ xxxCreateDialog.vue └─ xxxEditDialog.vue └─ ...```

 vscode 支持点击跳转文件的

组织结构-UserList - components - UserInfoDialog.vue UserList.vue封装Ref + Promise 封装成类似 ElMessageBox.confirm 的 Promise 化的 Dialog大概思路:  editor.csdn.net/md/?articleId=134161250

业务类的一般都放在 business 文件夹,和 components 文件夹相同级别

你这个场景 很适合插槽,如果是我的的话,就用插槽+组件的形式, 手动狗头~

比较推荐 #49 楼的结构

组件也可以分为通用组件和页面组件的啊src└─ components└─ views└──── view-a└──── components

这是我目前的开发方式。1.图中可见有个路由 /company/index, /company/edit, /company/detail 。在 company 下的 components 是抽离出来的独立性比较高的组件,也被其他几个路由少量引用2.investment 是我目前思路的典型做法。- data.ts 用于本模块下提供一些共用的局部字典什么的- 各目录下的 vue 一定是在 router 中定义的,components 内的除外- components 下的永远不会被路由直接访问,是供页面引用的组件- 从相应页面拆分出来的组件,以页面名称为前缀

两种方法,使用 JSON Schema 。使用 useDialog() 函数

文件夹套文件夹别的啥都不方便

 yes

感觉,你是需要函数式组件

 看起来很高级,第一次听说,谢谢,我去了解一下

函数式组件不能 jnject ,不方便我的最佳实践:- 每个页面都放在一个文件夹中而不是单文件组件- 每个文件夹下都放一个 __com__ 用来放页面模块- index.vue 只是用来把多个模块拼装在一起- 数据和逻辑用 provide/inject 共享复用- 也可以独立一个 js 文件存放( hooks 方式)

pageA└─ index.vue└─ __com__└─────── moduleA.vue└─────── moduleB.vue└─────── dialogA.vue└─────── drawerA.vue

我的做法,直接上代码 github.com/wurencaideli/dumogu-admin/blob/master/web/src/views/system/menu/components/EditDataDialog.vue

我们项目的做法,组件分为三级 1. /src/components/: 通用组件,具有跨项目的共性,更多是对通用组件库的补充;例如图片裁切组件,判断依据是不调用业务 API ; 2. /src/views/components/: 跨业务组件,多个业务都调用的基础组件,可以调用业务 API ;例如用户选择组件; 3. /src/views/users/components/: 业务自身组件,就是楼主所说的详情对话框、编辑对话框,放在对应路由下,类似 RESTful 的思路,按照资源进行组织的,当然,路由、Views 也是 RESTful 的思路进行组织的

/src/components/dialog.vue 通用的弹窗组件,里面封装通用的弹窗功能,集成修改业务功能,新增业务功能,props 参数,包含增,修改的接口,和表单组件。不传这些就是一个普通的弹窗。dialog 通过 ref 获取表单组件实例,调用表单校验获取数据等。/src/views/user/__controller__/cru-dialog.vue 具体业务组件,可以看做是一个具体实现的控制层,cru 就是 crud 中 cru 没有删除功能,当然也可以拆开,具体包含了,弹窗唤起增加,修改的业务逻辑,和详情的逻辑,在这里调用通用组件的 dialog 。注意这里只负责具体业务功能,不操作表单。/src/views/user/components/form/cru-form.vue 具体业务组件的表单,包含表单的所有操作,表单校验,根据实际情况是否拆分详情。为上面的控制器提供表单实体。这样分开之后,业务具体功能,表单解耦了,弹窗也是。当对外调用的时候,只需要调用 cru-dialog.vue 组件即可,这个弹窗已经包含了增,改,详情。由于表单是独立存在,当其他页面业务可能需要详情表单,也可以直接引用表单组件。这种有参考 MVC 模式的想法。

 得闲会拜读一下,谢谢:)

 分级分的很合理,目前看过的大多数开源 Vue 项目也是如此,谢谢:)

 函数组件可以 inject 只要用 inject 去写创建弹窗,别用 rander 去写就能拿到上下文了

我们项目的目录结构是这样划分的,src 根目录下有公共的 components 、composables 、api 、assets ,modules 文件夹下是每个模块,每个模块都可以有自己的 components 、composables 、路由、api 、类型、静态资源、国际化文件

 模块注册

我喜欢写个公共组件 ButtonDialog.vue ,然后把弹窗内容单独一个组件,用的时候就用 ButtonDialog 包裹下,这样 Edit 组件方便和 Detail 组件写一块,因为没有 Dialog 这一层,额外用的时候也很方便。ButtonDialog 默认是按钮,动态组件也方便传其他的