前端基于 Vue3 + Element Plus + Vite + TypeScript 。当前,每个页面都有类似于:详情对话框、编辑对话框和各种嵌套对话框等,该复用的已经复用过了,剩下的对话框都含有各自的特色内容,不宜复用,强行复用可能会导致单文件代码量过多不利于后期维护。
目前的疑问:我现在的做法是拆分每个 el-dialog
为单文件组件,存放到 components
文件夹下(可能有违 components
这个文件夹的定义,我的理解是 components
用来存放一些复用性很高的组件,而不是这种零散的对话框单组件文件)所以就导致我现在的项目结构很多 xxxDialog.vue
文件,既不知道如何归类存放,也不知道该怎么高效复用。
1
bsg1992 2023-12-03 12:12:16 +08:00
组件只用于某一个页面,不需要再进行封装了。
|
2
konakona 2023-12-03 12:15:55 +08:00
收藏了,想看看大家的做法。
|
3
giter OP @bsg1992 那存放这些页面对话框,只能放到 components 文件夹下?我想用更优雅一点的方式,不然太多 xxxDialog.vue 文件了,看着碍眼
|
5
giter OP @owen800q 确实是,有些对话框的内容都好长了,全放在一个页面 template 下的话,动不动就上千行代码,确实很不利于后面维护
|
6
owen800q 2023-12-03 12:26:43 +08:00 via iPhone
@giter 没办法。我们有三十多个 xxModal.vue, 一个 modal 都至少一百五十行代码, 不可以把他放在原来的页面,不然不好维护
|
7
LandCruiser 2023-12-03 12:28:11 +08:00 1
components 文件夹下是放公共组件的地方,不能公用的组件不要往那放。
比如商品浏览这个功能的所有组件,应该是在 src/views/GoodsList/目录下 这个目录下应该有/GoodsItem.vue GoodsDetail.vue GoodsDialog.vue 而不是把 GoodsDialog 放到/src/components/下去 |
8
giter OP @owen800q #6 难道只能这样滋生更多的 xxxModel.vue 或者 xxxDialog.vue 吗?太多类似文件感觉没充分复用,但强行复用又是另一个大麻烦
|
9
LandCruiser 2023-12-03 12:30:11 +08:00
还有就是你没有必要为了抽象而抽象,为了提取组件而提取组件。提不提去组件不是按代码行数来的,同样几百行代码,有时候提出去方便,有时候写在一起方便。
|
10
giter OP @LandCruiser 有想过这个问题,谢谢,后面如果实在没有更好的办法,我就把对话框组件文件挪到 views 页面同级目录下,毕竟 components 只适合放通用性组件文件
|
11
bsg1992 2023-12-03 12:34:57 +08:00
@giter
我的习惯是,基础组件以及业务类组件,放在 components 目录下。 基础组件按照功能进行文件夹划分 业务组件按照业务类型进行文件夹划分 如果业务中出现了强依赖的组件,直接扔到该业务页面下建立一个文件夹进行管理。 其实这种情况非常普遍,有一些业务功能看似能复用, 但是他往往带有非常强的业务特有场景,这个时候你在想去复用他其实是没有意义的。 |
12
bsg1992 2023-12-03 12:39:19 +08:00
在工作中也看到一些同事,为了代码的复用,拆成 N 个组件,然后组件中甚至暴露了 N 多个 slot ,这样反而本末倒置,为了抽象而抽象。
|
13
giter OP @bsg1992 #11 那确实没得好的办法了,官方对 components 文件夹的定义是可复用性组件,对 views 文件夹的定义是可通过浏览器访问的页面( vue-router 下引入的组件文件来自 views 目录),所以如果没有好的归类地方,那最好的应该还是随 views 一起存放,外层用文件夹包裹以区分不同的页面内容
|
14
giter OP @bsg1992 #12 也不是为了代码复用而拆分,而是页面内容太多不利于维护,拆分后可以做到一个页面只完成一项或多项类似的功能,全部糅杂在一个页面真的很头疼,尤其是对话框很多的情况下
|
15
zcf0508 2023-12-03 12:44:02 +08:00 via Android 2
组件单独抽象一方面是复用,一方面是解耦,把对话框单独挪出去可以保证对话框内的逻辑独立,然后在主页面引用多个独立的对话框组件,是挺好的实践
|
17
lmqdlr 2023-12-03 14:01:56 +08:00 via Android 2
components 下面是公共组件,页面目录放一个 components 存放页面组件
|
18
MrUser 2023-12-03 14:44:40 +08:00 2
这样?
./index.vue ./components/ ./dialogs/ ./list/index.vue ./list/dialogs/ ./detail/index.vue ./detail/dialogs/ |
19
humbass 2023-12-03 14:49:06 +08:00
整不是应该抽离出来,写成 Promise 的方式调用吗?
|
20
iOCZS 2023-12-03 14:58:42 +08:00
从某种程度上讲,dialog 也是一个 route 。。。
|
22
thevita 2023-12-03 15:28:18 +08:00 1
本质应该算是后端的非主流半吊子前端来说下:
我会习惯按业务逻辑相关性来放, 不通用 但 一起完成某个相关业务逻辑的放一个目录下 如: ..../(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 -- ( 只是举个例子,一般还是会叫具体完成的事) |
23
giter OP @thevita 看过多个开源的前端项目,都是这样的,最主要的页面取名 Index.vue ,剩下的如详情对话框,就取名 detail.vue ,编辑对话框就取名 edit.vue
|
24
CHTuring 2023-12-03 15:42:27 +08:00 1
不需要考虑那么多复用的情况,components 文件夹你只需要放公用的组件就好了,比如你在 Element Plus 的 Dialog 组件二次封装了组件。其它的页面内业务 Dialog 直接放在对应路由页面下的 components 文件夹或者不需要文件夹,直接 xxDialog.vue 或 xxModel.vue 就好了。
|
25
ruoxie 2023-12-03 15:43:09 +08:00 1
components 放业务无关的拆出去的组件,containers 放包含各种业务逻辑的拆出去的组件
|
26
CHTuring 2023-12-03 15:49:59 +08:00 1
1 、约束文件夹、文件的路径和命名规则
2 、公共的 hooks 、utils 、组件的文档编写 3 、异步引入减少加载时常和页面体积 4 、测试(可选) 只要规范这三个,不管是什么前端项目基本都清晰可维护了 |
27
LavaC 2023-12-03 15:56:37 +08:00 2
对话框内容不多的话可以改成命令式调用的方法,传几个参数改改里面的字就够了。类似于 element 里的 this.$message 。
如果各有业务内容的话那分成多个组件也是没有办法的事,不用内疚。 业务特有的组件的话,我们公司的习惯是 views 下分页面,比如 foo 页面,如果 foo 页面下有专有组件就在 foo 文件夹下再建一个 components 文件夹,结构大概是这样 views/ foo/ components/ BarDialog.vue index.vue |
28
LavaC 2023-12-03 16:11:44 +08:00 2
@giter #21 不知道其他人是怎么解决的,我前几个月也尝试看看能不能将 dialog 改造成类似 model 一样的命令式调用,以减少页面上那一堆管理弹窗状态/传值的代码,现在用下来感觉还不错。
|
30
anguiao 2023-12-03 18:42:56 +08:00
我每个小模块会有自己的 components 文件夹,不是公用组件的话我就丢在这里面了。
|
31
zogwosh 2023-12-03 19:42:55 +08:00
还有一种方法,写一个文件叫 xxxModal.tsx,把这个页面的所有用到的 modal 文件放里面.
|
33
nianyu 2023-12-03 21:21:38 +08:00
你最后就算封装成一个组件了,该写的还是少不了,json 配置 table form 看过吗,其实你定义的 schema 跟直接写 html 没什么区别的。
|
34
sjhhjx0122 2023-12-03 21:21:45 +08:00 2
弹出层这个事情也是为难了我很久,写到最后还是命令式调用,更符合直觉,你看看我写的这个库找找灵感 https://vue-modal-provider.netlify.app/
|
35
totoro52 2023-12-03 21:26:56 +08:00
过度封装带来的后果是更难的维护和组件的膨胀
|
36
giter OP @sjhhjx0122 谢谢,我研究一下你的代码
|
37
connection 2023-12-03 21:57:26 +08:00 1
首先弹窗组件的复用看功能,不从 ui 来划分复用。
然后是弹窗声明式编写,但是命令式调用。 |
38
TimPeake 2023-12-03 22:26:25 +08:00
我最讨厌那种页面拆分成 N 个组件的了....有想过后面接手兄弟的感受吗?得看这 N 个组件的属性传递、自定义方法等,对了,vscode 默认还不支持点击跳转到该文件,来回跳转头都大了... 真不如塞到一个页面里 。什么, 页面代码太长?那是你注释不清晰....分块集中写在一起也很好阅读
|
39
sjhhjx0122 2023-12-03 23:18:36 +08:00
@giter 我在写完这个库之后,就把弹窗当做页面,放在用弹窗的页面文件夹里,因为弹窗里面本身就承载像表单这种业务逻辑,只是他是命令调用不是当路由跳走的页面
|
40
bojackhorseman 2023-12-03 23:37:49 +08:00 via iPhone
我是拆组件,编辑和添加用一个组件,用 type 来区分,如果详情也是弹窗,相同的部分多的话就共用。
|
41
zbowen66 2023-12-04 01:14:24 +08:00
@LavaC #28 我之前也是这么写的,不过没有开源,我现在写 React 了,这是 React + antd 版的 https://bowencool.github.io/create-antd-modal/
|
42
Genshin2020 2023-12-04 08:40:35 +08:00 1
一样的技术栈,但是我把 element plus 中一个 input 组件就基于业务不同,封装了几十个业务组件,select 也是,封装了几十个,然后 form 表单是基于 一个配置信息 v-for 渲染出来的,用动态组件。
什么业务逻辑都在业务组件中封装好,校验什么的,核心就是只关注入参和出参,写好文档。 而表单配置信息,你乐意写在一个 config.ts 里都行,或者直接写在 index.vue 同级目录下。 |
43
lingyired 2023-12-04 09:21:18 +08:00
分情况,如果这个 dialog 的显示内容不需要特殊的布局和排版以及内容,那么可以使用命令式调用, 把 title, content ,buttons 传递进去。比如单纯的 alert, confirm 等。
对于需要特殊布局,然后还有数据驱动的,甚至是一些逻辑的,才抽离成一个 vue 组件。 |
44
umaker 2023-12-04 09:45:53 +08:00
我认为关键在于你如何定义 dialog 的多于少。
如果一个页面或者整个项目里要使用的 dialog 数量(按类型计算)会以数量级的方式增加,比如这次需求有 2 个弹窗,下次需求就可能加到 20 个或者更多,这种情况,是需要考虑如何从项目顶层设计才能更好地承载这样的业务模式; 如果只是递增形式的变多,那只是业务需求而已,按照需求的描述,做好基本的代码管理就可以了。 什么是好代码?不要让维护者看不懂你的代码就是好代码。 |
45
crazyTanuki 2023-12-04 09:47:41 +08:00
弄个 modalConfig.js 进行集中配置,自己二次封装太麻烦了
|
46
cenbiq 2023-12-04 09:53:48 +08:00
products
└─ components ├─ product-edit.vue └─ product-view.vue ├─ product-details.vue └─ product-list.vue |
47
cenbiq 2023-12-04 09:54:26 +08:00
```
|
48
SixGodHave7 2023-12-04 10:01:30 +08:00
当前页面路径下开个 components 文件夹
|
49
MENGKE 2023-12-04 10:04:16 +08:00 2
```
xxx └─ index.vue └─ components └─ xxxCreateDialog.vue └─ xxxEditDialog.vue └─ ... ``` |
51
we21x 2023-12-04 10:48:49 +08:00 1
组织结构
-UserList - components - UserInfoDialog.vue UserList.vue 封装 Ref + Promise 封装成类似 ElMessageBox.confirm 的 Promise 化的 Dialog 大概思路: https://editor.csdn.net/md/?articleId=134161250 |
52
davin 2023-12-04 10:55:59 +08:00
业务类的一般都放在 business 文件夹,和 components 文件夹相同级别
|
53
tog 2023-12-04 10:58:27 +08:00
你这个场景 很适合插槽,如果是我的的话,就用插槽+组件的形式, 手动狗头~
|
54
iMiata 2023-12-04 11:13:00 +08:00
比较推荐 #49 楼的结构
|
55
qwas 2023-12-04 11:59:38 +08:00 1
组件也可以分为通用组件和页面组件的啊
src └─ components └─ views └──── view-a └──── components |
56
JohnH 2023-12-04 12:17:49 +08:00 1
|
57
Cheez 2023-12-04 13:42:45 +08:00
两种方法,使用 JSON Schema 。使用 useDialog() 函数
|
58
rm0gang0rf 2023-12-04 16:07:59 +08:00
文件夹套文件夹别的啥都不方便
|
59
giter OP @rm0gang0rf yes
|
60
trokix 2023-12-04 16:28:14 +08:00 1
感觉,你是需要函数式组件
|
62
QuincyX 2023-12-04 17:34:41 +08:00 1
函数式组件不能 jnject ,不方便
我的最佳实践: - 每个页面都放在一个文件夹中而不是单文件组件 - 每个文件夹下都放一个 __com__ 用来放页面模块 - index.vue 只是用来把多个模块拼装在一起 - 数据和逻辑用 provide/inject 共享复用 - 也可以独立一个 js 文件存放( hooks 方式) |
63
QuincyX 2023-12-04 17:37:03 +08:00
pageA
└─ index.vue └─ __com__ └─────── moduleA.vue └─────── moduleB.vue └─────── dialogA.vue └─────── drawerA.vue |
64
wuzhanggui 2023-12-04 18:01:34 +08:00 1
|
65
AlphaTr 2023-12-04 18:33:54 +08:00 2
我们项目的做法,组件分为三级
1. /src/components/: 通用组件,具有跨项目的共性,更多是对通用组件库的补充;例如图片裁切组件,判断依据是不调用业务 API ; 2. /src/views/components/: 跨业务组件,多个业务都调用的基础组件,可以调用业务 API ;例如用户选择组件; 3. /src/views/users/components/: 业务自身组件,就是楼主所说的详情对话框、编辑对话框,放在对应路由下,类似 RESTful 的思路,按照资源进行组织的,当然,路由、Views 也是 RESTful 的思路进行组织的 |
66
as80393313 2023-12-04 19:23:33 +08:00 1
/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 模式的想法。 |
67
giter OP @wuzhanggui 得闲会拜读一下,谢谢:)
|
69
sjhhjx0122 2023-12-04 20:40:03 +08:00
@QuincyX 函数组件可以 inject 只要用 inject 去写创建弹窗,别用 rander 去写就能拿到上下文了
|
70
haodaking 2023-12-05 11:23:48 +08:00
|
72
Yjhenan 2023-12-05 11:55:31 +08:00
我喜欢写个公共组件 ButtonDialog.vue ,然后把弹窗内容单独一个组件,用的时候就用 ButtonDialog 包裹下,这样 Edit 组件方便和 Detail 组件写一块,因为没有 Dialog 这一层,额外用的时候也很方便。
ButtonDialog 默认是按钮,动态组件也方便传其他的 |
73
xingyuc 2023-12-05 16:19:51 +08:00
src/module/moduleName/views, components, routes, store...
这样每个模块的东西都在自己下面,也可以直接引用其他模块的组件 |