源码工作台:如何提效业务源码开发

1,454 阅读7分钟

原文地址:Nealyang/PersonalBlog

前言

无论lowcode再怎么🐂x,都避免不了对于复杂页面或者说特定页面的源码开发

之前也有写过相关文章总计:一张页面引起的前端架构思考,但是更多的是介绍How,并没有介绍到 Way,经过了一年的使用(rax 1.x 体系也在完善),必然也会伴随着一部分的调整。此篇作为阶段性总结以及对 BeeMa 架构开发辅助插件的铺垫。

以下介绍,主要是针对使用Rax 、TypeScript 的 H5 MPA 开发总结。

丐版

通常编码MPA 应用,都是在 pages 下新增相应page,然后在里面堆components。对于ajax 接口联调一般都是在 componentDidMount 或者 useEffect 中。虽说如此,但是比较宽泛。

团队中大多使用 rax 编码,在日常编码工作中就是 fn(state)=>UI的过程,所以在归类下来主要工作无非:

  • index.tsx 提供聚合
  • 请求接口拿到字段传递给各个组件
  • 组件展示、消化内部状态 or 协同合作(通信)

现状

如果没有规范的约束,那么每个人的风格都差别较大

可以看到,前端的业务编码无非就是如上三个问题,但是每个同学处理的方式都迥然不同,导致业务中每接手一个项目改动别的同学代码都需要花费一定能的时间去消化原有逻辑。

并且!如果涉及到多人合作的页面,可能还会有大量的代码冲突(页面逻辑并未高度解耦

问题与挑战

总结如上源码开发中团队合作遇到的问题:

  • 编码风格差异较大,接手老项目需要花费一定时间消化代码逻辑
  • 业务模块耦合度高
  • Bundle 较大,首屏加载、codespliting 缺失
  • 页面容器缺乏一致性,能力参差不齐

而针对如上问题,如果我们需要提供一套架构来解决这类问题,那么至少我们需要提供:

  • 页面容器(管理模块、基本页面功能封装)
  • 状态管理方案
  • 模块加载方案(模块高度解耦,避免多人协作冲突)
  • 如上功能抽成组件,代码仓库更专注于业务开发

Action

基础容器

从之前做过的项目中,我们总结容器应该具备如下能力:

API 说明

属性含义类型
title标题string
renderPlaceholder渲染占位层(loading)() => FunctionComponent
showPlaceHolder是否展示占位层(isLoading)boolean
hiddenScrollToTop隐藏回到顶部boolean
toTopProps回到顶部组件的属性IScrollToTopProps
renderHeader渲染头部组件() => FunctionComponent
renderFootr渲染底部组件() => FunctionComponent
customStyles自定义容器样式{contentWrapStyles,headWrapStyles,bottomWrapStyles}
onEndReachedThreshold距离底部多少距离开始触发 endReachedNumber

IScrollToTopProps

属性说明类型
bottom距离底部距离number
zIndexzIndexnumber
icon图片 icon 地址string
darkModeIcon暗黑模式的 icon 图片地址string
iconWidthicon宽度number
iconHeighticon 高度number
threshold滚动距离(滚动多少触发)number
animated点击回滚到顶部是否有动画boolean
right距离容器右侧距离number
onShow展示回调(...args) =>void
onHide消失回调(...args) =>void

基础的广播事件

名称含义参数
SCROLL滚动事件scrollTop 具体顶部距离
TRIGGER_ERROR触发 error 界面
END_REACHED触底事件
RESET_SCROLL重置滚动,重新计算容器高度
ENABLE_SCROLL禁止滚动true/fase

如上容器组件的封装,就提供了基本的容器能力。面对大部分的业务开发,基本都是能够满足需求的。

再次强调!!! 编写业务页面,其实完全可以把整体工作分为两趴:

  • format 数据
  • 拿数据渲染 UI

所以文章后面介绍的就是状态管理工具选型,以及如何整理状态,最后,如何加载模块

状态管理

有了基础容器提供的底层能力,再回想我们使用 reactvue 还是 rax 开发前端页面,其实都是状态驱动 UI 的过程 ,所以针对复杂业务的场景,状态管理自然必不可少。

基于现有的 hooks 技术方案,天然就存在状态管理解决方案:useRedux ,但是考虑到模块之间的高度解耦,还是非常有必要对 redux 进行改动,让其支持中间件、composecombineReducers等特性。所以针对第一版的架构设计,自己封装了一份状态管理方案:从 redux 的范式中搬个轮子做源码项目的状态管理

但是目前集团内,ice 提供了一套更加简易的状态管理封装,iceStore 并且 rax 也提供了支持。所以自然还是跟着集团的源码方向走,这里我们的状态管理,最终选择了使用 iceStore 的解决方案

对于状态管理,考虑到模块的高度解耦,约定每一个模块,对应着状态树的一个分支 , 简而言之,就是新增一个模块,要新增对应模块的 model

如上优点:

  • 状态统一管理,简单页面只需管理自己的 model 对应的 statedispatchers 即可
  • 跨模块通信可通过引入对应模块的 dispatchers 即可
  • 页面通用数据,比如宝贝 id 等,可通过 common model ,由框架层面统一分发到每一个模块中(模块加载部分介绍具体实现)

状态分发

讲解状态分发的前提应该先介绍下接口数据的请求配置。其实也比较简单,就是一个 mtopajax)请求拿到属于而已

架构中,将请求封装到 **utils** 里面,然后在自定义 **hooks :useDataInit** 中调用分发状态

请求接口数据

在源码架构初始化出来是一个模拟的请求,数据来自 page-name/mock/index.json

状态分发 use-data-init.ts

在自定义 hooks 中,拿到数据后,根据模块化字段,分发到对应的组件里面。

如上,我们已经完成了我们装备整个应用(页面)的状态的工作,下面我们的重点就是如何合理的根据状态树去加载模块

模块加载

模块加载,按照之前较为“随意”的编码方式,是根据各自风格,往 index.tsx 中一股脑的堆放,加持着各种 ifElse 的判断 这样存在的弊端如下:

  • index.tsx 入口杂乱
  • 页面耦合度较高,多人协作存在冲突
  • 久而久之可能会导致 index.tsx 较长,逻辑复杂

针对如上问题,我们希望:

  • 模块基于配置
  • 如果不涉及到公共逻辑或者页面级别的部分,index.tsx尽可能大家都不会涉及到修改
  • 模块能够异步加载,支持 code splitting

目录

src/page-name/components/

小总结

  • 编写业务页面,工作分为两步:1、拿到“自己满意”的 state 。 2、根据 state 去渲染 UI。所谓的各种交互也只是修改对应的 state 而已
  • 初始化状态在 use-data-init 里通过调用接口拿到数据,并且分发到各个模块里面。组成我们“想要”的状态树。
  • index.tsx 根据拿到的状态树然后基于 config.ts 来决定如何加载组件
  • 底层能力通过 pageContainer 组件支持
  • 状态管理方案选择 store,对应的 model 除了 pageStatecommon,其他就是每一个业务模块

重点强调

注释

Ts 中注释即文档。虽然模块高度解耦,但是哪怕自己再熟悉的模块,随着时间推移也有生疏的时候,所以尽可能的做到模块声明的每一个字段都加以注释

state 分支对应的模块需要与 config.ts 中配置保持一致

详细约束详见:拍卖源码架构在详情页上的探索

之所以不想详细介绍约束,是因为这里提供了一系列 vscode 插件按照插件的提供的功能去开发,即可消化架构层面带来的约束

解决方案

详细使用说明,下回分解~

创建应用

createPro.gif

支持 pc、无线、组件等应用脚手架 模板 EMS 配置

新建页面

以 h5 源码举例

createPage.gif

  • 根据应用类型,获取对应页面页面脚手架
  • 基础信息支持多种模板语言配置
  • 移动端支持基础UI配置(通用头、渐变背景、底部按钮等常规布局 UI)
  • 支持页面基础信息修改

模块配置

addSyncComp compConfig

  • 新增、删除模块
  • 模块支持首屏组件以及按需加载组件
  • 模块拖拽排序

BeeMa 大纲

BeeMa 大纲

方便快捷定位核心功能开发,近乎 96%的功能可以 focus 到此大纲中完成

如上举例和提效,基于 Rax MPA 源码页面开发