源码工作台:如何提效业务源码开发
关注并将「趣谈前端」设为星标
每天定时分享技术干货/优秀开源/技术思维
前言
❝无论
❞lowcode
再怎么🐂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 | 距离底部多少距离开始触发 endReached | Number |
IScrollToTopProps
属性 | 说明 | 类型 |
---|---|---|
bottom | 距离底部距离 | number |
zIndex | zIndex | number |
icon | 图片 icon 地址 | string |
darkModeIcon | 暗黑模式的 icon 图片地址 | string |
iconWidth | icon宽度 | number |
iconHeight | icon 高度 | 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」
❝「所以文章后面介绍的就是状态管理工具选型,以及如何整理状态,最后,如何加载模块」
❞
状态管理
有了基础容器提供的底层能力,再回想我们使用react
、vue
还是rax
开发前端页面,其实都是「状态驱动 UI 的过程」,所以针对复杂业务的场景,状态管理自然必不可少。
基于现有的hooks
技术方案,天然就存在状态管理解决方案:useRedux
,但是考虑到模块之间的高度解耦,还是非常有必要对redux
进行改动,让其支持中间件、compose
、combineReducers
等特性。所以针对第一版的架构设计,自己封装了一份状态管理方案:从 redux 的范式中搬个轮子做源码项目的状态管理
但是目前集团内,ice
提供了一套更加简易的状态管理封装,iceStore 并且 rax 也提供了支持。所以自然还是跟着集团的源码方向走,这里我们的状态管理,最终选择了使用iceStore
的解决方案
对于状态管理,「考虑到模块的高度解耦,约定每一个模块,对应着状态树的一个分支」, 简而言之,就是新增一个模块,要新增对应模块的model
如上优点:
状态统一管理,简单页面只需管理自己的 model 对应的 state
和dispatchers
即可跨模块通信可通过引入对应模块的 dispatchers
即可页面通用数据,比如宝贝 id 等,可通过 common model
,由框架层面统一分发到每一个模块中(模块加载部分介绍具体实现)
状态分发
讲解状态分发的前提应该先介绍下接口数据的请求配置。其实也比较简单,就是一个mtop
(ajax
)请求拿到数据而已
架构中,「将请求封装到「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
除了pageState
和common
,其他就是每一个业务模块
重点强调
注释
Ts 中「注释即文档」。虽然模块高度解耦,但是哪怕自己再熟悉的模块,随着时间推移也有生疏的时候,所以尽可能的做到「模块声明的每一个字段都加以注释」
state 分支对应的模块需要与 config.ts 中配置保持一致
❝详细约束详见:拍卖源码架构在详情页上的探索
❞
之所以不想详细介绍约束,是因为这里提供了一系列 vscode 插件,「按照插件的提供的功能去开发,即可消化架构层面带来的约束」
解决方案
❝详细使用说明,下回分解~
❞
创建应用
❝支持 pc、无线、组件等应用脚手架 模板 EMS 配置
❞
新建页面
❝以 h5 源码举例
❞
根据应用类型,获取对应页面页面脚手架 基础信息支持多种模板语言配置 移动端支持基础UI配置(通用头、渐变背景、底部按钮等常规布局 UI) 支持页面基础信息修改
模块配置
新增、删除模块 模块支持首屏组件以及按需加载组件 模块拖拽排序
BeeMa 大纲
❝方便快捷定位核心功能开发,近乎 96%的功能可以 focus 到此大纲中完成
❞