目录
前言
我写代码一直有个毛病就是代码耦合性太高,可能是性子太急,总觉得前期设计组件结构会花费太多时间(其实是我能力不足),还有不自信自己最后设计出来的公共组件会提高效率,害怕得不偿失。但是今天遇到了很明显的需要抽离出一个公共组件的情况……我记录下来自己的设计思路,可能不是很准确,欢迎路人善意的指教。
内聚性和耦合性
偶然间刷到知乎的一个关于架构设计的文章,引用其中对我来说印象深刻的观点。详情指路: 【架构设计】如何让你的应用做到高内聚、低耦合?
内聚
关于代码的内聚性通常有7种描述
#mermaid-svg-1edqrPvcBcd3K5Ho {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-1edqrPvcBcd3K5Ho .error-icon{fill:#552222;}#mermaid-svg-1edqrPvcBcd3K5Ho .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1edqrPvcBcd3K5Ho .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-1edqrPvcBcd3K5Ho .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1edqrPvcBcd3K5Ho .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1edqrPvcBcd3K5Ho .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1edqrPvcBcd3K5Ho .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1edqrPvcBcd3K5Ho .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1edqrPvcBcd3K5Ho .marker.cross{stroke:#333333;}#mermaid-svg-1edqrPvcBcd3K5Ho svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1edqrPvcBcd3K5Ho .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1edqrPvcBcd3K5Ho .cluster-label text{fill:#333;}#mermaid-svg-1edqrPvcBcd3K5Ho .cluster-label span{color:#333;}#mermaid-svg-1edqrPvcBcd3K5Ho .label text,#mermaid-svg-1edqrPvcBcd3K5Ho span{fill:#333;color:#333;}#mermaid-svg-1edqrPvcBcd3K5Ho .node rect,#mermaid-svg-1edqrPvcBcd3K5Ho .node circle,#mermaid-svg-1edqrPvcBcd3K5Ho .node ellipse,#mermaid-svg-1edqrPvcBcd3K5Ho .node polygon,#mermaid-svg-1edqrPvcBcd3K5Ho .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1edqrPvcBcd3K5Ho .node .label{text-align:center;}#mermaid-svg-1edqrPvcBcd3K5Ho .node.clickable{cursor:pointer;}#mermaid-svg-1edqrPvcBcd3K5Ho .arrowheadPath{fill:#333333;}#mermaid-svg-1edqrPvcBcd3K5Ho .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1edqrPvcBcd3K5Ho .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1edqrPvcBcd3K5Ho .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-1edqrPvcBcd3K5Ho .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-1edqrPvcBcd3K5Ho .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1edqrPvcBcd3K5Ho .cluster text{fill:#333;}#mermaid-svg-1edqrPvcBcd3K5Ho .cluster span{color:#333;}#mermaid-svg-1edqrPvcBcd3K5Ho div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1edqrPvcBcd3K5Ho :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
高
内聚性
低
功能内聚顺序内聚通信内聚过程内聚时间内聚逻辑内聚偶然内聚
#mermaid-svg-8SriOqCJcPzVXzYr {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8SriOqCJcPzVXzYr .error-icon{fill:#552222;}#mermaid-svg-8SriOqCJcPzVXzYr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8SriOqCJcPzVXzYr .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-8SriOqCJcPzVXzYr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8SriOqCJcPzVXzYr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8SriOqCJcPzVXzYr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8SriOqCJcPzVXzYr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8SriOqCJcPzVXzYr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8SriOqCJcPzVXzYr .marker.cross{stroke:#333333;}#mermaid-svg-8SriOqCJcPzVXzYr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8SriOqCJcPzVXzYr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8SriOqCJcPzVXzYr .cluster-label text{fill:#333;}#mermaid-svg-8SriOqCJcPzVXzYr .cluster-label span{color:#333;}#mermaid-svg-8SriOqCJcPzVXzYr .label text,#mermaid-svg-8SriOqCJcPzVXzYr span{fill:#333;color:#333;}#mermaid-svg-8SriOqCJcPzVXzYr .node rect,#mermaid-svg-8SriOqCJcPzVXzYr .node circle,#mermaid-svg-8SriOqCJcPzVXzYr .node ellipse,#mermaid-svg-8SriOqCJcPzVXzYr .node polygon,#mermaid-svg-8SriOqCJcPzVXzYr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8SriOqCJcPzVXzYr .node .label{text-align:center;}#mermaid-svg-8SriOqCJcPzVXzYr .node.clickable{cursor:pointer;}#mermaid-svg-8SriOqCJcPzVXzYr .arrowheadPath{fill:#333333;}#mermaid-svg-8SriOqCJcPzVXzYr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8SriOqCJcPzVXzYr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8SriOqCJcPzVXzYr .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-8SriOqCJcPzVXzYr .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-8SriOqCJcPzVXzYr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8SriOqCJcPzVXzYr .cluster text{fill:#333;}#mermaid-svg-8SriOqCJcPzVXzYr .cluster span{color:#333;}#mermaid-svg-8SriOqCJcPzVXzYr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8SriOqCJcPzVXzYr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
强
模块独立性
弱
如果一个页面没有任何公共组件那么就是一个偶然内聚的情况,所有功能集中在一个模块中,这也是我以前写过的,不过在工作中很快就被纠正了,因为实际工作中大多是很多模块有着相同功能,这个时候每个页面都要写一遍的话太费时间了。下面我写的例子我认为可以归为功能内聚。
耦合
#mermaid-svg-m0QbAv6eBZEFIazF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-m0QbAv6eBZEFIazF .error-icon{fill:#552222;}#mermaid-svg-m0QbAv6eBZEFIazF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-m0QbAv6eBZEFIazF .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-m0QbAv6eBZEFIazF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-m0QbAv6eBZEFIazF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-m0QbAv6eBZEFIazF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-m0QbAv6eBZEFIazF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-m0QbAv6eBZEFIazF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-m0QbAv6eBZEFIazF .marker.cross{stroke:#333333;}#mermaid-svg-m0QbAv6eBZEFIazF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-m0QbAv6eBZEFIazF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-m0QbAv6eBZEFIazF .cluster-label text{fill:#333;}#mermaid-svg-m0QbAv6eBZEFIazF .cluster-label span{color:#333;}#mermaid-svg-m0QbAv6eBZEFIazF .label text,#mermaid-svg-m0QbAv6eBZEFIazF span{fill:#333;color:#333;}#mermaid-svg-m0QbAv6eBZEFIazF .node rect,#mermaid-svg-m0QbAv6eBZEFIazF .node circle,#mermaid-svg-m0QbAv6eBZEFIazF .node ellipse,#mermaid-svg-m0QbAv6eBZEFIazF .node polygon,#mermaid-svg-m0QbAv6eBZEFIazF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-m0QbAv6eBZEFIazF .node .label{text-align:center;}#mermaid-svg-m0QbAv6eBZEFIazF .node.clickable{cursor:pointer;}#mermaid-svg-m0QbAv6eBZEFIazF .arrowheadPath{fill:#333333;}#mermaid-svg-m0QbAv6eBZEFIazF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-m0QbAv6eBZEFIazF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-m0QbAv6eBZEFIazF .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-m0QbAv6eBZEFIazF .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-m0QbAv6eBZEFIazF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-m0QbAv6eBZEFIazF .cluster text{fill:#333;}#mermaid-svg-m0QbAv6eBZEFIazF .cluster span{color:#333;}#mermaid-svg-m0QbAv6eBZEFIazF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-m0QbAv6eBZEFIazF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
低
耦合性
高
非直接耦合数据耦合标记耦合控制耦合外部耦合公共耦合内容耦合
#mermaid-svg-n1nEpUdVyhUMQq3Z {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .error-icon{fill:#552222;}#mermaid-svg-n1nEpUdVyhUMQq3Z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-n1nEpUdVyhUMQq3Z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .marker.cross{stroke:#333333;}#mermaid-svg-n1nEpUdVyhUMQq3Z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-n1nEpUdVyhUMQq3Z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .cluster-label text{fill:#333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .cluster-label span{color:#333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .label text,#mermaid-svg-n1nEpUdVyhUMQq3Z span{fill:#333;color:#333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .node rect,#mermaid-svg-n1nEpUdVyhUMQq3Z .node circle,#mermaid-svg-n1nEpUdVyhUMQq3Z .node ellipse,#mermaid-svg-n1nEpUdVyhUMQq3Z .node polygon,#mermaid-svg-n1nEpUdVyhUMQq3Z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-n1nEpUdVyhUMQq3Z .node .label{text-align:center;}#mermaid-svg-n1nEpUdVyhUMQq3Z .node.clickable{cursor:pointer;}#mermaid-svg-n1nEpUdVyhUMQq3Z .arrowheadPath{fill:#333333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-n1nEpUdVyhUMQq3Z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-n1nEpUdVyhUMQq3Z .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-n1nEpUdVyhUMQq3Z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-n1nEpUdVyhUMQq3Z .cluster text{fill:#333;}#mermaid-svg-n1nEpUdVyhUMQq3Z .cluster span{color:#333;}#mermaid-svg-n1nEpUdVyhUMQq3Z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-n1nEpUdVyhUMQq3Z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
强
模块独立性
弱
技术框架
示例用的是Taro React框架以及TaroUI。
感觉TaroUI很灵活,很多实现可以自己去定制化开发。
需求描述
1、列表搜索功能
2、列表下拉加载,触底分页请求列表数据
上面的需求涉及到了3个不同页面,并且这3个页面的列表接口不是同一个。
设计思路分析
本来是想直接用Taro React的
useReachBottom
这个事件监听滚动列表是否触底的,奈何这个事件并没有监听到,并且
usePageScroll
这个事件也没有监听到,去github的issue板块查也没有确切的结果,只能用ScrollView滚动视图来替代了,这也是Taro的一个容器组件,只要设置容器高度就可以通过
onScrollToLower
监听到是否滚动到底部。
首先是父组件引用我写的公共组件
CommonScrollView
很明显给子组件传了3个值:
1、service是每个页面的列表请求接口,类型是
(params: any)Promise<any>
2、showSearch,
boolean
是否存在搜索栏(这个暂时没有不存在的情况,这个目前意义不大)
3、renderList,
({ searchVal, dataList }: { searchVal: string, dataList: any[] }) => React.ReactNode
这个方法会接收公共组件请求列表接口返回的列表数据,不同页面拿到数据可以在自己的页面写渲染方法,还接收一个search Value,是因为搜索空的空白展示图和普通空白展示图不一样,用来判断展示不同图片的条件。
三个页面的请求参数都是相同的,搜索值(
value
)、第x页(
pageNo
)、每页x条(
pageSize
)
所以我干脆把请求参数也放到公共组件里了,不然每个页面还要维护一套pageNo和pageSize的状态,实际公共组件负责更改pageNo(触底+1)
<CommonScrollView
service={getProspectusPage}
showSearch
>{renderList}</CommonScrollView>
renderList:
constrenderList=({searchVal, dataList}:{searchVal: string,dataList: IProspectus[]})=>{if(!dataList.length){if(searchVal){return(<View className={styles.noData}></View>)}return(<View className={styles.noData}></View>)}return dataList.map((item: IProspectus)=>(<View ></View>))}
也就是说我把滚动触底触发分页查询请求,以及搜索触发接口请求和页面初始化时的列表接口请求都放到公共组件
CommonScroll
里面了,页面只负责拿这个子组件传过来的列表数据进行渲染即可。
而且当我写好一个页面之后,剩下两个页面花了1分钟就解决了!不得不感慨一下前期的组件设计真的很重要!
下面是我公共组件的代码结构
interfaceIProps{children:({ searchVal, dataList }:{searchVal: string,dataList: any[]})=> React.ReactNode,service:(params: any)=> Promise<any>,showSearch: boolean;}constCommonScrollView=(props: IProps)=>{const{ children, service, showSearch =false}= props
const[searchVal, setSearchVal]= useState<string>('');const[currentList, setCurrentList]= useState<any>([]);const[loading, setLoading]= useState<boolean>(true);const[pageNo, setPageNo]= useState<number>(1);const[dataReachBottom, setDataReachBottom]= useState<boolean>(false);useDidShow(()=>{fetchData('',1)})const fetchData =(val ='', searchPageNo?: number |undefined)=>{setLoading(true);const currentPageNo = searchPageNo || pageNo;
service?.({param: val,pageNo: currentPageNo,pageSize:10}).then((res: any)=>{setLoading(false);let _currentList =[...currentList,...res.data.list];if(searchPageNo ===1){
_currentList = res.data.list;}setCurrentList(_currentList);if(_currentList.length === res.data.total){setDataReachBottom(true);}else{setPageNo(currentPageNo +1)}});}constonScrollToLower=()=>{if(!dataReachBottom){fetchData(searchVal)return;}};consthandleChange=(val: string)=>{setSearchVal(val);}consthandleSearch=()=>{setPageNo(1)fetchData(searchVal,1)}consthandleClear=()=>{setSearchVal('');fetchData('',1)}return(<Fragment>{showSearch &&<AtSearchBar
value={searchVal}
onChange={handleChange}
onActionClick={handleSearch}
onClear={handleClear}/>}<ScrollView
scrollY
className='scroll-view'
scrollWithAnimation
onScrollToLower={onScrollToLower}
style={{height:'80vh'}}>{children({ searchVal,dataList: currentList })}{loading &&(<View style={{position:'relative',height:'5%'}}><AtActivityIndicator content='加载中...' mode='center'></AtActivityIndicator></View>)}{(dataReachBottom &&!searchVal)&&(<AtDivider
content='没有更多了'
fontColor='#676874'
lineColor='#ECECEC'/>)}</ScrollView></Fragment>);}exportdefault CommonScrollView;
这样看来,如果这些代码在每个页面都写一次,真的很臃肿,用我前同事的话来讲就是:“很僵硬”。
版权归原作者 芝麻馅圆圆 所有, 如有侵权,请联系我们删除。