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;
}
参考文章