复杂前端系统开发的难点和重点

从广告投放管理系统,到房屋设计审批系统,ERP 系统,税务报表管理系统,众筹信息管理平台,研发流程管理系统,作前端这些年我开发了不少这样的系统,如今你们好像喜欢叫他们中后台系统,或者 B 端系统,我以为他们是比较典型的复杂前端系统,这篇文章但愿经过个人经验总结一下开发这些系统时的难点和解决难点时须要关注的重点。前端

本文主要关注功能的场景和模型,而不是技术选型,技术方案对解决问题有帮助,可是不会改变问题的实质,好比用 Redux 相比用 jQuery,须要发的请求一个也不会少,只是在数据向视图的转化和事件的绑定等具体操做上有了简化。浏览器

那么什么样的系统是复杂的前端系统?开发中又有哪些难点呢?

从交互上直观地看,咱们能够看到有如下典型元素的系统常常是更复杂的。表单,弹窗,标签页,分页,加载更多,表格等。那么这些元素为何会使系统变得复杂呢?咱们逐一看一下:框架

表单

众筹管理平台的建立筹款表单
众筹管理平台的建立筹款表单

从传统网站开始,表单就是系统的难点,它的复杂在于用户要经过它输入数据,这还涉及到咱们你们都熟悉的表单验证。在用了 Redux 等工具以后,问题获得了必定程度的简化,问题的模型也变得更清晰,表单的每一个项目都是一个状态值,与用户的输入绑定,而各个表单项目之间还可能有关联逻辑,甚至是关联的数据请求,好比用户选择了这个项目的值以后,须要请求相关子项目的数据。还有咱们常见的 input suggestion,根据用户输入实时获取数据给出可选项。表单校验又会给每一个表单项增长错误相关状态,包括是否有错误和错误信息等。在表单基础上更复杂的交互有表单配合弹窗,表单嵌套表单等,主要模型是针对某个表单项须要选择或者新增相关信息,好比表单中有地址项,而地址项能够新增,这时候就涉及到模块之间的通讯,在信息新增完成后须要根据新增信息更新表单数据。ide

标签页

很是常见的元素,主要是监听点击事件作模块之间的切换,复杂在于每一个模块均可能有本身的初始化等逻辑,而模块之间又可能有关系,或者在某些状况下须要在切换时重置模块状态。函数

分页,加载更多

也是比较常见的元素,作的是模块内部数据的更新和增长,接收用户的点击事件,请求相应数据。工具

表格

广告投放系统中的表格
广告投放系统中的表格

展现型的表格复杂度不高,通常只是有初始化的请求,数据加载后就直接渲染了。复杂的表格有两个方向:fetch

  • 内部操做型,常见的好比针对每一条数据的删除按钮,甚至点击删除按钮后须要有确认弹窗或者确认浮层,这就须要有更多的状态来控制了。还有有时候可能会有编辑按钮,经过弹窗或者将表格行切换成输入框来进行更新,这时候就把表单揉进来了。
  • 外部操做型,好比常见的复选框 与外部操做按钮,针对选中行作操做,还有常见的针对表格的筛选。
  • 除了这两个方向外,其实还有单纯前端展现特性上的难点,好比一屏放不下的表格有时候会有滚动页面时表头跟随的需求,不过这种难点并非本文关注的重点。

小结

经过上面对这些典型元素的分析,我将这些元素增大系统复杂度的缘由归纳为两个方面,一是增长了模块的数目,二是增大了模块内部的复杂度。而模块内部的复杂度又涉及到用户事件,数据请求和数据状态三个方面,这三个方面又是互相关联的,好比用户事件常常会改变系统数据状态,也常常触发数据请求,数据请求返回的数据通常也会做为数据状态的一部分。网站

模块

这个是一个功能相对独立的功能块。在技术上一般表现为 Redux 一个子 Store 对应的部分,它通常有本身的一些状态(state),可能须要发一些请求去获取一些初始数据。通常模块的生命周期是发请求作数据的初始化,响应用户事件改变内部状态,也可能会再发一些数据请求,有时可能须要对自身状态进行重置或者从新发出初始化时的数据请求对自身数据进行刷新,在生命周期结束时还可能须要清理本身的状态数据。设计

模块之间的通讯

模块增长以后的系统复杂度增长不是线性的,可能因为模块之间的互动造成几何式增加,而模块之间的通讯的方法也是前端关注的一个重点,Flux 模型解决的一个很重要的问题就是模块间通讯方式混乱,在 Flux 数据单向流动模型下模块之间的常见通讯方式精简为如下两种。cdn

  • 经过 Action。由于 Flux 系统中系统的主要状态所有在 Redux state 中,于是只要经过 Action 改变相应 state,相应模块就会接收到改变,这些改变的影响能够直接反映到视图上,或者相应模块能够监听属性变化,在适当状况下作出 side effect 响应。
  • 父子通讯。父模块能够经过传递属性值让子模块拿到最新的数据状态,经过传递属性函数让子模块能够随时通知父模块本身的状态变化。

模块之间通讯的时机通常在模块初始和结束时,这样的模块关系好比兄弟步骤关系,上一步在结束时会把本身的信息存储好并开启下一步,或者父子关系好比上面咱们提到过的模块中填写地址时调用新建地址模块新增地址,新增地址模块结束后把新增地址信息传递回来。

若是模块之间有频繁的通讯,那么他们极可能能够概括在同一个大的模块下,好比上面提到的表格和它外部的操做和筛选按钮。

模块之间公用逻辑的复用

模块多了就避免不了出现重复性的逻辑,好比权限校验,多个模块可能须要一样的对操做者权限进行校验并在无权限时给出提示的逻辑,还有例子好比多个模块都须要根据鼠标的移动位置让组件执行必定的逻辑。目前 HOC 和 React Hooks 是解决复用问题的主要方案,复用虽然不能让模块自己的复杂度下降,可是能够减轻咱们开发多个一样功能时的工做量。

用户事件

这包括输入框,按钮等触发的咱们常见的事件好比 onChange, onClick 和 onScroll 等等,他们毫无疑问是整个前端系统的核心,由于它们表明用户的操做,而用户的操做是系统运转的入口,增长了入口天然增长了复杂度。

数据请求

十年前 Gmail 开创先河向咱们展现了 Ajax 可以在浏览器上作出什么样的应用,开启了前端大发展的十年。那为何它是特别的?一直到如今咱们的 WEB 应用的核心功能是什么?其实仍是随时随地的数据通讯能力和与之配合的局部刷新,这也是为何 WEB 应用有一个名称叫作 RIA —— Rich Internet Application,而数据请求的层次多少和密度大小也是模块复杂度的重要因素。固然如今咱们的数据请求不止 Ajax 一种,还有 WebSocket 和 SSE 等,他们会让数据请求的逻辑变得更加复杂。

有些状况咱们的模块不能只经过一次请求得到所有所需数据,好比接口琐碎或者数据源复杂,就须要拼数据,好比请求了列表信息还得去请求每一个 item 的详细关联信息,因此有 GraphQL 解决接口 under-fetching 和 over-fetching 的问题,服务端提供数据,要解决请求的问题,改造服务端是一个重要的途径。固然若是服务端不容易改造,不少时候前端仍是得本身解决问题。

Redux Saga 和 Redux Observable 就是为了解决复琐事件和数据请求逻辑而产生的,对 Redux 而言也就是处理 Action 和反作用(Side effects)。

Redux Saga 能够用它的 Effects 简单地描述竞争性请求(Race),不阻塞请求(Fork),等待所有请求完成(All)等;能够控制包括数据请求在内的动做顺序,好比典型的 Login Logout 流程,只有登陆以后才能够作登出操做;还能够在 Action 层内部经过 Put 和 Take 作订阅模式,好比在第一个 Saga 中定义发起数据请求先后一系列动做的执行流程,而在第二个 Saga 中定义数据请求自己的流程,好比到多个地方获取数据作组装,第一个 Saga 会经过 Put 发布数据请求动做,第二个 Saga 会经过 TakeEvery 订阅这个动做作出反应。

Redux Observable 和它背后的 RxJS 的事件控制能力则更强大,好比能够轻松地监听 Triple Click 事件,能够定义多个事件的组合触发关系,好比定义多个事件必须都发生过才能够触发总体事件,然后续任何一个再发生都会触发总体事件。

事件和数据请求,或者说是 Action 和 Side effects,他们的复杂度不只取决于数量,更取决于他们之间的时序和逻辑关系。

状态

现代框架中的 state 概念,是单向数据流动的核心,React 和 Redux 等工具中都有,相信你们都能理解,系统须要的 state 越多也就表明系统更加复杂。

总结

模型

说了这么多,你们可能会想,这些东西有什么用呢?我认为上面的模型基本上抽象出了咱们要解决问题的框架,咱们在接到一个新系统的需求或者一个新的功能需求时,能够先构思一下有哪些模块,模块之间是否有通讯,须要在什么时机进行通讯,多个模块之间是否有公用代码。针对每一个模块,须要想一下模块是否须要初始化数据和重置数据,模块内部的用户事件有哪些,获取数据的逻辑和时机如何,模块须要记录的状态有哪些。作了这些思考以后咱们对系统的复杂度就有了一个大体认识,对于评估工做量和作排期计划都是有帮助的,更重要的是若是不提早想好这些重要的细节咱们可能会在开发的后期才发现他们,他们甚至可能一直被忽略于是引发线上 Bug,模块的重置和数据请求的时序逻辑是常见的容易被忽略的问题。

除此以外,咱们能够根据模型给出一些典型的功能场景,而后对比用不一样技术框架来实现功能时的优缺点,这样便于咱们对比具体的代码,而不是凭空地争论。

最后,但愿这些思考可以抛砖引玉,欢迎你们批评指正。

相关文章
相关标签/搜索