基于dva的表单类页面复用思考

背景

新学期新开始,前几天老师让我和一个学弟一起接手实验室新接的项目,老师已经把基本框架搭好了,项目是基于dva的金融类管理系统(所以表单类页面又来了)

现有代码的问题

这个管理系统涉及到很多列表类页面,而当我们将model层以视图划分时,会发现现有代码有很多很多类似的。而且,为了保持代码风格差不多,当我加入项目时,先尝试写了一个基本curd功能的表单页,发现写的很痛苦。因为,差不多只能按照他的模式来写,整个编码体验就像是戴着脚链跳舞。
而更让人难过的是,只要是基本crud功能的页面,捎带夸张一点的说,即使你完全不会dva只要按着相应流程复制粘贴代码都可以实现一个基本crud页面。

talk is cheap ,show me the code
// 后端接口
基于dva的表单类页面复用思考_第1张图片
// 按照接口写好相应api
基于dva的表单类页面复用思考_第2张图片
然后定义路由、在routes下写页面级组件、models下写相应视图类的model,每个页面对应一个命名空间。真正重复的地方在model那里,每个model几乎一样的方法,每个方法内几乎一样的赋值操作(不涉及业务逻辑的页面),归根结底还是一样的问题,因为不一样的后端接口而去重复大量的代码。
当我给老师提了我的想法后,老师很开明地给了我时间去尝试(先可以考虑只包含最基本curd功能的表单优化)!^ ^

探索

  1. 很快想到可以使用上次的高阶组件组装toolbar和list的方法,只是获取数据等完全抛开model
    但,不想这样做,因为这样的改进让代码显得格格不入。
  2. 但是有一点是可以肯定的:serivce里的函数方法调用的api一定是变量化的,即不重复写一个页面级的增删查改api
    理论支撑来源于: 页面级的基本功能对应的api字段几乎相似,就只有页面级的关键字段不一
    基于dva的表单类页面复用思考_第3张图片
    基于dva的表单类页面复用思考_第4张图片

3.思路1:
高阶组件接收一个页面组件,是动作转发者,响应用户动作,数据来源同样来自一个models,单独定义一个公用models
困难点:
1. 公用model的载入问题
2. 高阶组件无法connect数据源
3. 高阶组件选型也特别困难
若是无状态组件,那么监听什么使得组件获得初次载入值呢?
class组件,可以利用生命周期监听组件挂载

4.思路2:
不再纠结高阶组件(集合所有需要复用公用办法的页面级组件,统一调度一个公用model),换个角度:为什么不页面级组件均引入同一个models呢?即不需要高阶组件包裹,我自己可以实现直接传当前的路径名给models层

困难点:
models层监听函数怎么监听订阅数据源组件的挂起?(以前是直接监听路由的变化)
即问题转变为:models层的划分问题感谢dva issue里xufei的回答(原来不只我有这个困惑)
我们现在代码冗余的地方就是model层那里,他的建议(确实也是我们现在做的),model层跟着视图划分,如一个页面为一个model,复用的实现交给下级service。
问题现状:
1.几乎所有页面都存在增删查该操作,而我们几乎都要因为一点小的变动去重复大量代码
2.除去增删查改的基本操作,有些特定业务逻辑也在不同视图出现。
为了简化写我们可能会这样写:
同样的effect操作下的x动作,我们可能会复用相同的service函数,即传当前url使得在service那层调用不同url,实现一定复写功能。

举例:
有个电话信息填写的页面(功能一致),只是展示的数据不一样。
比如用户电话页补充、账户注册时的电话补充、企业注册的电话补充,来源均是在电话详细页的上一级XXphone列表点击某行update携带id跳转页面得到。即根据id调用不同的url获取原视图对应的详细信息。
我们所做的复用努力:所有电话详细页只有一个页面级组件+model提供,而判断具体是哪种电话详细页则是通过路径携带页面信息的参数:/phone/:id/:type,根据携带的type我们在下级的service层依据type匹配具体的请求url。
至于不同组件使用相应model的setup监听问题的解决:
const match = pathToRegexp(‘/phone/:id/:type’).exec(pathname.pathname)
只要匹配了如上格式的路径,则发出query的信号

综上分析

我们在现有代码基础上寻求最小改动的思考:
1.将公用增删查改操作封装成单独的models,某组件需要则引入该model
2.或是我们现在做的:写一个pagemodel[增删查改、换页等],其余组件的model继承自该model,但是公用的pagemodel只写了reducer的更新状态函数,并没有写异步调用的effect,因为effect调用的service函数对应的url是写死的,所以只能每次都新写effect

==》尝试:修改pagemodel,将effect调用的service函数公有化,但是每个页面级组件还是要新建个单独的model文件,因为这一变量:监听的路由; 或是能实现组件挂载(那么每个页面级组件都要变成有生命周期的状态组件)则能实现组件一挂起就订阅数据的功能

==》综上分析:最好能每个组件仍能无状态(最小改动原则)、页面级组件还是无状态的,但是高阶组件是有生命周期的,页面级无状态组件实现数据订阅,高阶组件负责监听组件挂起,并dispatch query信号

===》 解决: 使用另一种写法的高阶组件,
解决了如下问题:
1.在定义页面级组件时仍保证页面级组件的无状态性
2.首次挂载获取数据的问题:不在models层监听路由判断,而是在组件挂起时dispatch信号
3.你可能会说这不是”既要牛挤奶又不要牛吃草”吗? 既要保持无状态性还要它有生命周期?
===》 关键点:高阶组件
传入一个无状态组件,我们在高阶组件内部定义一个class组件,该class组件render传入的无状态组件。我们将无状态组件通过高阶组件(其实是函数)包裹起来,此时返回该class状态组件即可.即我们将组件状态化这个过程暗箱操作了.解决了这个关键性问题,接下来就是connect数据的问题了,同样,我们在高阶组件里实现数据订阅

总结:
万里长征迈出了最关键的第一步,接下来就是业务逻辑的判断了
解决了不同无状态组件订阅同一数据源、以及在组件一挂载的监听问题,
最大的成就就是在现有基础上实现最小改动:相当于我们暗箱操作了这些改动。将改动降低最小(即优化成本的问题)
最小改动:setup监听由model监听路由变为高阶组件下监听组件挂起,此时发起首次query,而其他操作如删除、修改等依然可以在原页面级组件里dispatch信号。

最后:
完成了之前答应老师的任务,接下来可以好好准备下面试什么的了。。此刻撰笔写下此文想的是如果面试官面试之前看到这篇文章可能面试时沟通障碍要小点吧(我怕我一紧张就说不清楚自己的思路T T)

最最后,思考的时候自言自语大法好啊!
附思考笔记:
基于dva的表单类页面复用思考_第5张图片

其实是未完待续

我们解决了基本crud组件的model复用问题,接下来需要考虑扩展性、list组件化、弹出框组件化等等..

你可能感兴趣的:(代码优化思考,----------React)