liuxx-u / bird-front

bird前端项目,基于react、antd、antd-admin,封装常用数据组件,细粒度权限解决方案。
281 stars 110 forks source link

跟我之前的思路是一样的 也想模板化 但是有很多坑用这种是解决不了的 #6

Closed fangkyi03 closed 6 years ago

fangkyi03 commented 6 years ago

我前几天就用这种思想去搭建了一个框架 全部采用ts去编写 前期没有出现什么大问题 很顺利 但是后期功能越来越复杂的时候 就出问题了 而且用了ts以后 发现这样写 类型定义会非常的混乱 因为你全部的这种方式 parent>children>children 这里面有可能嵌套会非常的深 导致你用ts对这个模板进行类型定义完全失去了意义 因为这个模板当你组件越来越复杂的时候 就只能公用一套类型定义 导致很多比如tab组件中出现了select组件独有的属性等等 没有办法做到好的分离

其次比如modal层弹出一个页面 上面有表单组件 当你在下拉菜单中选中一个数据的时候 要给另外的编辑框进行赋值 这时候就只能在onSelect中去处理

我对这种表单事件部分 做了两种处理 如果返回的是对象 如{a:value}这种对象的 就直接刷新对应的表单组件数据 如果返回数组 如[{url:'aaa'}] 就发起一个网络请求队列来实现

我放弃这个的最大原因就是耦合性太高了

以一个tab组件来说 就有非常多的事件需要外抛出来 会显得非常的混乱 如table头部按钮组的点击事件 如新建 编辑等 这里还要区分多选跟单选 table底部的分页点击事件 table最右侧操作部分 如单个选中编辑 单个选中删除等 又或者table表单每个列悬浮的时候 要显示对应的提示内容等

业务不复杂的时候 的确有简化 但是业务复杂起来的时候 代码乱的要死

而且即使强如ts 也无法对这种模板做类型的限制了

有兴趣的可以加我qq:469373256 一起交流一下

liuxx-u commented 6 years ago

@fangkyi03 table中默认的新增/编辑弹框确实不能满足所有的定制化需求。对于定制化要求高的页面,可以让开发者通过自定义按钮的方式来实现,目前表格的行内按钮以及右上角的全局按钮均支持自定义。

1、关于分页、排序、筛选等事件是约定了接口的格式,所以在组件内部可以完全处理。 2、关于区分多选、单选问题,表格定义了很多种数据类型,多选,单选,日期,数字等。不同类型渲染不同的组件。 3、关于table右侧操作部分,单个选中编辑/删除等按钮是交给开发者自己去自定义的。组件内部只默认了新增和查询按钮。

在做组件封装的时候兼顾灵活性。

fangkyi03 commented 6 years ago

组件分类我尝试了 这种方式是不可行的 可能你在js中写代码 没有察觉 但是当你用ts以后 你会发现 所有的参数都是混乱的 用ts都无法区分每个组件的独有属性

liuxx-u commented 6 years ago

@fangkyi03

我的想法是提供项目中大部分场景下的表格模板与默认实现,而对于定制要求较高的场景提供灵活的自定义方式。目前这个表格在我们公司用起来反响也还不错。

liuxx-u commented 6 years ago

组件分类还有一个好处,其实不仅仅可以应用于表格,还可以应用于表单。 [{ "name": "申办单位全称", "isRequired":"true", "key": "crmCustomer.name", "fieldType": "text" }, { "name": "开发渠道", "key": "crmCustomerExtend.developChannel", "fieldType": "dropdown", "source":{"url": "xxx"} }] 类似这样一个配置传递至表单组件,即可完成表单的渲染以及值改变事件,可以直接取整个表单的值。这个配置可以存在于json文件,甚至是服务端都可以,能够完成线上表单的动态调整。

限制依然和表格一样,对于定制要求较高的场景需要开发者自己来开发。组件只提供大部分场景的模板与默认实现。

fangkyi03 commented 6 years ago

你这个思路我早就考虑过了 我原来就是跟你一样的想法的 但是实际是什么 首先 你如果table中有新建 删除 等一些按钮 你按下的时候 需要弹出一个modal 这里就涉及到数据传递的问题了 你有些时候要会先数据 有些时候要把对应的下拉菜单 或者级联菜单数据进行回显等等 当你在modal中点击确定的时候 要重新刷新列表页面等 这种直接用模板就会很麻烦

其次 你说的后台json 的确 用来创建组件是可以 但是点击事件呢 不行了把 这个时候 你想给指定组件绑定就不行了 这个问题我早就考虑过了

fangkyi03 commented 6 years ago

其次表单不单单只是录入数据 还有比如select选择某一个值以后 要给另外几个表单组件进行修改赋值等 你这里在你这个模板里面写 就会有一大堆的点击事件

fangkyi03 commented 6 years ago

还有一个场景是什么 比如你有一个用户组 是一个树形的table 你直接把所有的用户组数据都读取下来是很不现实的 所有只能是在每个树形点击的时候 请求一次新的网络然后返回对应的数据 并且在列表里面进行刷新 这些操作会让你的template越来越臃肿 后期代码耦合性会越来越高 因为你的内部 已经无法去限定模板组件的类型了 你说用any 但是这个是完全没意义的 你还有组件定义的意义吗 还有用ts的意义吗

fangkyi03 commented 6 years ago

主要在于两点 1.抽象公用的事件以后 你会发现 很多不必要的组件 都被继承了这个事件 如一个onSelect事件 你用ts写的时候 你会发现 给这个组件加一个onSelect事件不会报错 但是实际上这个组件是根本不会允许有这个事件的 这种就会有很大的问题 所以问题就是用ts 根本无法做这种类型管理 如果用js就更加的混乱了 代码随便写 编译运行的时候都不会报错 等真正出现问题的时候才会发现 哦 原来这个组件没有这个事件

2.模板化以后 将所有的点击事件都集合在一块 这会导致你的template非常难以维护

fangkyi03 commented 6 years ago

对于这个弹出modal的时候 我当时就根据对应的一个modelName直接可以动态绑定了 原先设想是在每个form.create()用onValuesChange将所有的表单数据改变映射到对应的model中 因为antd的form表单的确 数据的流向不清楚 会出现明明赋值了 但是组件没有回显的问题 这也是我加这个参数的原因

前期都很ok 包括回显也都没问题 直到在select中你想去接管onSelect 或者onChang的时候 这个时候我触发了外部template的事件 并且在里面发起一个dispatch修改对应modal中的其他几个编辑框内容 想通过修改数据源的方式 直接给form表单赋值

但是最后发现 我发起的dispatch的确成功了 但是同时也会触发 onValuesChange事件 但是这时候在这个函数里面返回的props是上一次的数据 这就导致我通过数据源的方式去修改表单的数据失败了 变成了之前的数据

还有数据转换部分 如日期类型或者级联 或者下拉等等 有些时候需要进行一下数据转换 我当时在最外层加了一个函数 只要提供对应的方法名字 就会对网络请求以后的数据进行数据转换 但是后来发现 依旧是非常的麻烦 因为你需要做的事情太多了 数据转换以后 还要考虑表单数据合并 你回显的时候 也要回显转换以后的数据 并且每个组件都需要跟对应的key去进行绑定等等

太多 太多的问题了

liuxx-u commented 6 years ago

@fangkyi03

  1. 新增、编辑弹框时传递列配置以及当前行数据,然后根据类型去对相应控件进行赋值这些复杂度都不高的。 2.modal点击确定后,刷新数据。这个重新调用下查询方法。 3.弹框、查询组件等应该继续拆分为功能独立的小组件,把功能拆分之后其实没那么难。 4.关于表单select改变之后修改其他组件的值。我的做法是,一个对象维护表单的状态,需要做的是字段A的值变化同时修改这个对象B的值即可。 5.关于用户组树型table的问题,这种场景让开发者自定义开发就好,没必要全部定制化需求都在组件内部实现。 6.关于any类型,我是指字段的值可能会是很多种不同的类型,所以才用any,不是所有定义都是any。

我没用ts来写这个组件,甚至没有用dva的机制来写,是原生react的写法。可能用其他方式写会有些问题是我没有遇到的。但我想如果考虑好组件封装与开发者自定义的边界,应该也是能做的。我最开始做这个也遇到了很多很多的问题,但都慢慢解决了,甚至在公司推行使用过程中也做了很多修改与功能完善,但现在我们用起来真的还是非常舒服。

不是不能做,只是前期很难做,跨过这个坎,后面就轻松了。如果有兴趣,可以看看我项目中的源码:/components/Grid与/components/Form中的组件。