【组件开发笔记】组件の组织
1、组件设计基本原则
单一职责。
也就是:
① 降低组件的复杂度,减少代码量,提升可读性。
② 降低与其他组件的耦合度(低耦合),从而降低变更导致的对功能的影响。
③ 提高可复用性,功能单一(高内聚),有明确的边界,不访问其他组件的内部细节,接口最小化,单向数据流...。
2、TIPs
性能:无状态函数组件 > 有状态函数组件 > class组件;
减少props传递;
抽取过多的条件控制流;
不过度优化;
3、分类
【展示组件】-【本质:视图】
通用的组件:开发时,要以“第三方组件库”的标准考虑其设计,不和任何项目的业务耦合。
项目通用的组件:可多个组件共享,可能与业务有较低耦合程度。
组件特有的组件库:与业务深度耦合,不能与其他组件共享。
【容器组件】-【本质:逻辑】
侧重业务处理,通常为高阶组件。
数据来源:直接请求接口或从外部传入。
然后展示完整的视图。
【有状态组件&无状态组件】
无状态组件完全由外部props控制状态。如果想要优化它的props映射,提升性能,可以使用React.memo避免无用渲染。
React.memo类似于React.PureComponent,两者的区别为:React.memo是用于函数组件的,React.PureComponent是用于类组件的。
React.memo的用法为:
1 2 3 4 5 6 | const Component = ()=> { return ( <div>嘎嘎嘎</div> ) } const MemodFuncComponent = React.memo(Component) |
React.PureComponent 的用法为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import React from 'react' ; class Test extends React.PureComponent { constructor(props) { super (props); this .state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log( 'componentWillUpdate' ) } componentDidUpdate(prevProps, prevState) { console.log( 'componentDidUpdate' ) } render() { return ( <div> { this .state.count } <button onClick = { () => this .setState({ count: 1 }) }>点击</button> </div > ); } } export default Test; |
React在进行组件更新时,如果发现这个组件是一个PureComponent,它会将组件现在的state和props和其下一个state和props进行浅比较,如果它们的值没有变化,就不会进行更新。
类似于写了一个shouldComponentUpdate生命周期,判断
nextProps和
nextState是否有变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import React from 'react' ; class Test extends React.Component { constructor(props) { super (props); this .state = { count: 0 } } componentWillUpdate(nextProps, nextState) { console.log( 'componentWillUpdate' ) } componentDidUpdate(prevProps, prevState) { console.log( 'componentDidUpdate' ) } shouldComponentUpdate(nextProps, nextState) { // 如果没变化,就不渲染,否则重新渲染 if ( this .state.count === nextState.count) { return false } return true } render() { return ( <div> { this .state.count } <button onClick = { () => this .setState({ count: 1 }) }>点我</button> </div> ); } } export default Test; |
【纯组件&非纯组件】
纯组件其实类似于纯函数的概念。
纯组件就是[单向],给定输入,就永远返回相同的输出,过程没有副作用与外部的状态依赖(props,state,context没有变化,则输出没有变化),也就代表着这个组件不需要重新渲染,从而提高性能收益。
而很多个组件组成的比较复杂的组件树,则需要外部去维护它的状态,然后通过注入依赖,展示相对应的视图,这样可以维持组件树的纯净性。比较典型的解决方案是Redux,它把组件树看做一个状态树,分离逻辑与视图,再配合异步处理,就可以实现单向的数据流。类似的还有Cycle.js,有空学习。(#^.^#) (但还是附个链接: http://cyclejs.cn/#-%E7%BB%84%E4%BB%B6%E5%8C%96)
【布局组件&内容组件】
内容组件通常被约束在布局组件的占位中。抽离开来可以避免互相影响,提高可维护性。
布局组件:Grid, Layout, HorizontalSplit……
内容组件:Button, Label, Input……
【表单组件】
沿用通用的属性,保证兼容,减少代码重复,使用方便。
组件受控。实际开发中非受控组件的场景非常少, 自定义组件应只提供完全受控表单组件,避免组件自身维护缓存状态。
4、目录
大牛推荐的多页面目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | src/ components/ # 共享组件 containers/ Admin/ # 后台管理页面 components/ # 后台特定的组件库 LoginPage/ index.tsx ... App/ components/ # App特定的组件库 LoginPage/ # App页面 index.tsx stores.ts # redux stores AnotherApp/ # 另外一个App页面 hooks/ ... app.tsx # 应用入口 anotherApp.tsx # 应用入口 admin.tsx # 后台入口 |
。。。。。。
还有一些没用过的目录划分方式,有空学习。(#^.^#)
5、模块
每个目录都是一个模块,都应该有一个index的唯一出口文件来统一管理模块的导出(限定模块的可见性)
(除了utils文件夹,它只是一块模块命名空间,因为它目录下是互不相关或不同类型的文件)
相对路径不超过两级(即../
和./
)
或者将相对路径转为绝对路径, 例如webpack
中可以配置resolve.alias
属性来实现(直接copy一下大佬的代码):
1 2 3 4 5 6 7 8 9 | ... resolve: { ... alias: { // 可以直接使用~访问相对于src目录的模块 // 如 ~/components/Button '~' : context, }, } |
然后就可以:import { hide } from '~/utils/dom';
6、组件拆分
①拆分为render方法,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 生成数据列表工具栏 const renderToolBar = () => { const toolBarList: ReactNode[] = [ <Btn key= "showMetrics" type= "primary" onClick={showMetrics}> 自定义指标 </Btn>, rowKey === 'key' && <DownloadEfforts columnsData={columnsData} totalList={totalList} tableInfo={tableInfo} searchParams={searchParams} key= "downloadExcel" dataType= "effect" />, rowKey === 'materialId' && <DownloadEfforts columnsData={columnsData} totalList={totalList} tableInfo={tableInfo} searchParams={searchParams} key= "downloadExcel" dataType= "materialRank" />, (rowKey === 'adId' || rowKey === 'creativeId' ) && <DownloadEfforts columnsData={columnsData} tableInfo={tableInfo} searchParams={searchParams} key= "downloadExcel" dataType= "relation" />, ]; return toolBarList; }; return ( <BasisTable ... operateToolBar={() => renderToolBar()} /> ) |
但是这个方法只是在return处简化了代码,其实并没有真的实现组件的拆分。
当代码行数超过300这个阈值,就要进行组件进一步拆分。
②拆分为组件
- 纯UI渲染拆分为组件
- 纯逻辑内容拆分到hooks中 || 拆分到高阶组件中
- 两者皆有:将相关视图&逻辑都抽离,形成一个独立组件
7、组件文档化
https://storybook.js.org/
推荐用storybook.js进行文档化,用过,还是挺好上手的,就不赘述了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具