React的组件化开发

363 阅读5分钟

什么是组件化开发?组件化开发的思想

如果现在需要我们开发一个大型的页面,如果我们将所有的处理逻辑放在一起,那么这个页面的逻辑就会变得极为复杂且不利于后续管理。但是如果我们把一个界面 拆分 成一个个小的模块和功能。每个功能块完成属于自己独立的功能,那么之后整个页面的管理和维护就变得非常容易了。

所以,我们在进行React或Vue开发的时候要使用组件化的思想

优势

  1. 将一个页面拆分为多个组件,方便管理和维护
  2. 组件可以复用
  3. 每个组件都有对应的逻辑和其功能

React的组件和Vue的组件

React的组件和Vue的组件相比更加灵活,按照不同的方式可以分成很多类组件,例如:

  • 根据组件的定义方式:函数组件和类组件
  • 根据组件内是否有状态需要维护:无状态组件和有状态组件
  • 根据不同的职责:展示型组件和容器型组件

类组件

类组件的定义需满足如下要求:

  1. 组件的名称大写字符开头
  2. 类组件需要继承来自React.Component
  3. 必须实现render函数

函数式组件

函数式组件有如下特点:

  1. 没有this对象
  2. 没有内部状态
  3. 没有生命周期

组件的通信

无论是Vue还是React,在组件化开发过程中也需要相互进行数据的传递,最基本:父组件给子组件传递数据,那么React是如何实现这个的呢。

父组件向子组件传递数据使用的是props,详细看如下例子:

//父组件
//..
class Father extends React.Component {
    render(){
    	return (
        	<Childcpn name="fengan" age="18" height="185cm" />
            <Childcpn2 name="fengan" age="18" height="185cm" />
        )
    }
}

//类组件演示代码 - 子组件
class Childcpn extends React.Component {
    render(){
    	let { name,age,height } = this.props
    	return (
        	<div> { name+age+height } </div>
        )
    }
}

//函数组件演示代码 - 子组件
function Childcpn2(props) {
	const {name,age,height} = props
	render(){
    	return (
        	<div> { name+age+height } </div>
        )
    }
}

propTypes属性验证

在组件传值时候使用propTypes库对传入组件的值进行属性验证或设置属性的默认值:

如下代码对propTypes库的使用进行简单的指引:详细可参考React官网

//1.导入prop-types库
import PropTypes from 'prop-types'

//2.对子组件进行验证
//组件声明时的名字.属性
Childcpn.propTypes = {
	name:PropTypes.string.isRequired,
    age:PropTypes.number,
    height:PropTypes.number,
    names:PropTypes.array,
}

//除了可以验证还可以指定默认值
Childcpn.defaultProps = {
	name:"fengan",
    age:18,
    height:1.85,
    names:['aaa','bbb']
}

子组件传递数据给父组件

现在,我们知道了父组件如何传递数据给子组件,那么在我们开发的时候,时常也需要子组件传递数据给父组件。那么React如何实现的呢?

原来阿,React中同样是通过props来实现子组件传递消息给父组件的,只是父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;详细看如下代码

如何使用prop传递函数:

//子传父通信
//子组件
class Btnclick extends Component {
    render() {
        let { addClick } = this.props
        return <button onClick={ addClick }>+1</button>
    }
}

//父组件
class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            count:0
        }
    }

    render() {
        let { count } = this.state
        return (
            <div>
                <div>{ count }</div>
                //使用子组件,并传递父组件的函数到子组件
                <Btnclick addClick={ ()=>{ this.addClick() } } />
            </div>
            
        );
    }

    addClick() {
        let { count } = this.state
        count += 1
        this.setState({
            count
        })
    }
}

export default App;

案例:实现子传父

//父组件
import React, { Component } from 'react'
import TabControl from './TabControl'

export default class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            titles:['新款','精选','流行'],
            currentTitle:'新款'
        }
    }

    render() {
        let { titles,currentTitle } = this.state
        return (
            <div>
                <TabControl titles={ titles }  itemClick={ (index) => { this.getTitle(index) } } />
                <h2>{ currentTitle }</h2>
            </div>
        )
    }

    getTitle(index) {
        let { titles,currentTitle } = this.state
        currentTitle = titles[index]
        this.setState({
            currentTitle
        })
    }
}
//子组件
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import './style.css';

export default class TabControl extends Component {
    constructor(props) {
        super(props)
        this.state = {
            currentIndex:0
        }
    }

    static propTypes = {
        titles: PropTypes.array.isRequired,
        itemClick: PropTypes.func.isRequired,
    }

    render() {
        let { titles,itemClick } = this.props
        let { currentIndex } = this.state

        return (
            <div>
                <ul className="tab-control">
                    { titles.map( 
                        (item,index) => 
                        <li key={index} 
                            className= { currentIndex === index ? 'active tab-item' : 'tab-item'}
                            onClick={ e => { 
                                this.itemClick(index);
                                itemClick(index)
                            } }
                            >
                            {item}
                        </li>) }
                </ul>
            </div>
        )
    }

    itemClick(index) {
        this.setState({
            currentIndex:index
        })
    }
}
//样式文件
.tab-control {
    display: flex;
    padding: 0;
    margin: 0;
}

.tab-item {
    flex: 1;
    list-style: none;
    text-align: center;
    line-height: 44px;
    height: 44px;
}

.tab-control .active {
    border-bottom: 2px solid orange;
    color: orange;
}

总结: 原来实现子传父的形式是:父组件使用props传递函数到子组件,子组件调用这个函数。在子组件中把需要传递数据放入到props接受到的函数参数中。那么父组件的这个函数的参数就是子组件参数过来的数据,之后在再函数中对子组件传递过来的数据实现相应的代码。

在React中实现Vue的插槽

这里需要先给大家补充一个知识点: 我们在使用子组件的时候可以使用双标签的书写方式,在双标签中写的内容,会自动的传递到子组件的props.children属性中

//父组件
class App extends Component {
	render() {
    	return (
        	<div>
            	//双标签的形式使用子组件
                <NavBar>
                	<a>aaa</a>
                    <span>bbb</span>
                </NavBar>
            </div>
        )
    }
}

//子组件
class NavBar extends Component {
	render() {
    	return (<div>
        	<div>{this.props.children[0]}</div>
            <div>{this.props.children[1]}</div>
        </div>)
    }
}

上述案例大多推荐只传递一个组件进去的时候使用,而如果需要传递多个组件,上述的方式有个弊端就是需要按照一定的顺序写标签。所以针对传多个插槽,我们提供另外一种方式:使用props传递属性,属性值为需要插入对应地方的标签。以达到具名插槽的作用。

跨组件通信Context

Context是React提供用于在组件之间共享此类值的方式的API,而不必显示式地通过组件树的逐层传递props。

详情可参考官网文档