React笔记(五)
1.组件化
-
React应用采用基于组件的架构方式,也就是说可以将一个复杂的页面分解成一个个较简单的组件来实现。但组件在开发时,常常会遇到一些问题,比如为单一组件赋予了过多的指责。这在项目上是可行的,但如果需要修改现有功能,或者创建新功能,就大大增加了工作量。
export default class Demoe extends Component { state={ count:0 }; render() { return ( <> <div>{this.state.count}</div> <ControlCount parent={this} mode='add'/> <ControlCount parent={this} mode='sub'/> </> ) } } class ControlCount extends Component{ render(){ if (this.props.mode =='add'){ return <button onClick={()=> { this.props.parent.setState( (pre)=>({count:pre.count+1}) ) } }>加一</button> } else{ return <button onClick={()=> { this.props.parent.setState( (pre)=>({count:pre.count-1}) ) } }>减一</button> } } }
上面示例中,将两个按钮抽取成为两个组件,实现了应用的组件化,但如果需要增加功能,比如说,增加乘法与除法;又或者在其他地方需要使用此组件,但需要将+1变成+2。这个时候就需要做一些比较大的改动。
-
所以作为组件需要符合以下几点要求
- 单一责任 也就是说让一个组件尽可能的只执行一个任务,例如请求远程数据,又或者渲染表数据等。具有单一职责的组件,相对于其他组件,更容易编码,修改与测试。并且,多重责任的组件也会使得修改组件时副作用更难预测和控制,也就是当需要修改一处时,可能会在无意识的情况下修改了其他的部分。
- 封装 通过封装来减小耦合度。并且在实际应用时,可以实现重用性,与可替换性。整个组件由父组件传递进的
props
进行控制,在props
中可以传递字面量,对象,函数,甚至一个组件。另外,封装后的组件也要实现可组合性,其可以类似积木一般,一块一块组合起来。 - 纯净 这里的纯净代表着纯函数的纯,也就是当组件接收到相同的props时,其返回的组件内容是相同的,不纯净的组件会导致组件结果的难预测与难确定性。
-
在实际开发中,存在有很多组件库帮助我们实现了组件的封装,例如Material-UI:当下流行的 React UI 框架 (mui.com),Ant Design - 一套企业级 UI 设计语言和 React 组件库,Home - Fluent UI (microsoft.com)等。这些组件库帮助我们不需要手动去封装组件,但也根据实际应用需要而基于这些组件库去做二次封装。
-
简单封装一些组件
-
分页组件
export default class Demo extends Component { render(){ var list = []; for (let index = 0; index < 100; index++) { list.push({title:`标题${index}`,content:`xxxxxxxx${index}`}); } return <PagesList list={list} currentPage={1}/> } } class PagesList extends Component{ constructor(props){ super(props); //定义当前页码位置 this.state = { currentPage:props.currentPage }; } render(){ const {list} = this.props; const {currentPage} = this.state; //获取总页码数,并准备渲染 const totalPage = list.length/10; var pages =[]; for (let index = 1; index <= totalPage; index++) { pages.push(index); } //展示的列表 const showList = list.slice((currentPage-1)*10,10*currentPage); return ( <div> {/* 展示列表 */} <div style={{marginBottom:5}}> {showList.map((e)=>{ return <div className='message'><h3>{e.title}</h3><p>{e.content}</p></div> })} </div> <div> {/* 判断是否展示上一页 */} {currentPage>1&&<a className='pages' onClick={ ()=>{ this.setState( (pre)=>({currentPage:pre.currentPage-1}) ) } } >上一页</a>} {/* 展示页码,并高亮当前选择的页 */} { pages.map((e)=>{ return <a className={e==currentPage?'pages active':'pages'} onClick={()=>this.setState({currentPage:e})}>{e}</a> }) } {/* 判断是否展示下一页 */} {currentPage<totalPage&&<a className='pages' onClick={ ()=>{ this.setState( (pre)=>({currentPage:pre.currentPage+1}) ) } }>下一页</a>} </div> </div> ) } }
.message{ border:1px solid black; } .pages{ padding: 5px; border:1px solid blue; } .active{ background-color: gold; }
-
消息展示组件
export default class Demo extends Component { render() { var list=[]; for (let index = 0; index < 100; index++) { list.push(`xxxxxxxx${index}`); } return <ShowNewMessage list={list}/> } } class ShowNewMessage extends Component { constructor(props) { super(props); //是否显示新消息部分 this.state = { contentVis: false, } } render() { const { list } = this.props; const {contentVis} = this.state; return (<> <button onClick={() => { this.setState({ contentVis: !contentVis }) }} className='open'>打开消息</button> {/* 通过状态控制消息展示 */} {contentVis && <><div className='messages'> { list.map((e) => { return <p className='message'>{e}</p> }) } </div> {/* 查看全部跳转新页 */} <button onClick={()=>{window.open('https://www.baidu.com')}} className='open'>查看全部</button> </>} </>) } }
.message{ border:1px solid black; } .messages{ width: 150px; height: 200px; overflow-y: scroll; } .open{ width: 150px; border:1px solid black; }
-
参考文章