组件

168 阅读10分钟

1. 创建组件

  1. 在创建组件的时候,组件的名称必须是大写,在组件引入使用的时候,也是需要大写,【 注:在react中,书写HTML的时候,必须是小写,虽然HTML大小写不区分,但是在react中,大写JSX会认为这个是一个组件的引入】,所以在需要书写HTML的时候,保持小写
  2. 占位符,在编写【React】代码的时候,在【vue】里面,是使用的是【template】,在react中使用【Fragment】进行占位,【占位符,在真实的渲染时,是不会被渲染成真是的DOM】,react中的Fragment也可以是被代替的,【<></>】进行占位。
import React, { Fragment } 'react'
使用的时候,对组件进行包裹即可,无需书写最外层的div

1.1 函数组件

1. 创建函数组件 】
import React from 'react'

function App() {
  return (
     <div></div>
  )
}
export default App2. 组件中使用 】
import App from './App.js'

1.1.1 函数组件之间的数据传递(父传子)

  1. 父组件通过【属性=值】的方式向子组件传递数据
  2. 子组件通过【props】接收父组件传递过来的数据
1. 单个数据的传递 】
import React from 'react'

function ChildenS(props) { 【 子组件通过props接收参数,也可以在接收父组件参数的时候进行解构{name} 】
	const { name } = props 【 或者接收porps,在重新进行解构 】
  return (
  	<div>{name}</div>
  )
}

function Father() {
  return (
  	<ChildenS name="给子组件传递数据"></ChildenS>
  )
}

【 2. 多个参数的传递,在开发的过程中,可能不仅仅传递一个数据,是多个参数,如何进行传递 】
const obj = {
	name: '向子组件传递数据',
  data: [
    {
    	id: 1,
      name: '向子组件传递数据',
      title: 'react'
    }
  ]
}

function Father() {
  return (
  	<ChildenS {...obj}></ChildenS> 【 通过解构语法,将对象传递过去,子组件通过props接收即可 】
  )
}

【 3. 如果父组件不传递数据,但是子组件需要使用,可以设置默认值  】

ChildenS.defauleProps = { * 函数组件之间参数传递,不传参数设置默认值
  name: '你好',
  age: 18
}

1.1.2 函数组件之间的参数传递(子传父,与类组件相同)

1.2 类组件

1. 创建类组件 】
import React,{Component} from 'react'

class App extends Component {
  render() {
    return (
    	<div></div>
    )
  }
}
export default App2. 组件中使用 】
import App from './App.js'

1.2.1 类组件的生命周期

  1. 组件挂载成功后【componentDidMount】
    • 依赖于DOM的操作可以在这个声明收起函数进行
    • 在挂载完成后,发送网络请求
  2. 组件更新【componentDidUpdate】
    • 会在组件更新后,立即被调用,首次渲染不进行调用
    • 当组件更新后,可以去操作DOM
  3. 组件卸载【 componentWillUnmount 】
    • 当组件销毁的之前直接调用
    • 在调用方法的时候,必要的操作是【清理操作】

1.2.2 类组件之间数据传递(父传子)

  1. 父组件通过【属性=值】的方式向子组件传递数据
  2. 子组件通过【props】接收父组件传递过来的数据
1. 单个数据的传递 】
import React,{Component} from 'react'

export default class Father extends Component{
   render() {
    return (
    	<div>
      	<ChildenS name="给子组件传递数据" ></ChildenS> 【 调用子组件 】
      </div>
    )
  }
}

export default class ChildenS extends Component{
   render() {
    const { name } = this.props; 【子组件通过this.props 接收父类传递过来的数据】
    return (
    	<div>{name}</div>
    )
  }
}

【 2. 多个参数的传递,在开发的过程中,可能不仅仅传递一个数据,是多个参数,如何进行传递 】
export default class Father extends Component{
  render() {
    const obj = {
      name: '向子组件传递数据',
      data: [
        {
          id: 1,
          name: '向子组件传递数据',
          title: 'react'
        }
      ]
    }
    return (
    	<div>
      	<ChildenS {...obj}></ChildenS> 【 通过解构语法,将对象传递过去,子组件通过props接收即可 】
      </div>
    )
  }
}

【 3. 父组件没有给子组件传递参数,但是子组件需要使用值,子组件设置默认值 】
export default class ChildenS extends Component{
  static defaultProps = {  【使用静态属性进行操作】
      name: '设置默认数据'
  }
  render() {
    const { name } = this.props  【需要进行解构,才可以正常使用】
    return (
      <div>{name}</div>
    )
  }
}
  1. 在类组件中,没有【.defauleProps】 的操作,但是类组件中有静态属性的方式,defauleProps

1.2.3 类组件之间参数传递(子传父亲)

  1. 在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;
1. 类组件事件传递 】
import React,{Component} from 'react'

export default class Father extends Component{
   render() {
     return (
    	<div>
                 【 属性=值的方式将事件传递给子组件,只不过属性后面传递的是事件 】
      	<ChildenS handole={this.handole.bind(this)}></ChildenS> 【 调用子组件 】
      </div>
    )
  }
  handole (父组件接收到数据) {
  	【 处理子组件传递过来的数据 】
  }
}

export default class ChildenS extends Component{
  render() {
    const { handole } = this.props;
  	return (
    	<div>
      	<button onClick={() =>handole(给父组件传递数据) }>触发事件</button>
      </div>
    )
  }
}

1.3 PropTypes参数验证(大型推荐使用TypeScript)

  1. 如果你项目中默认继承了Flow或者TypeScript,那么直接就可以进行类型验证
  2. 如果项目中没有集成Flow或者TypeScript,可以通过【PropTypes】对参数进行类型显示
1. 下载PropTypes  npm i prop-types -D】
【 2. 在组件中使用,子组件中使用,设置参数接收的类型 】
import PropTypes from 'prop-types'
import React,{Component} from 'react'

export default class ChildenS extends Component{
  render() {
    return (
    	<div></div>
    )
  }
}

【 2.1 指定接收的数据类型是什么,类型校验(函数组件类组件通用) 】
ChildenS.propTypes = {
  num: PropTypes.number, 【指定传递参数的类型】
  name: PropTypes.array.isRequired 【指定传参的类型,并且是必须传递的参数】
} 

【 2.2 类组件的第二种写法 】

export default class ChildenS extends Component{
  static propTypes = {
    num: PropTypes.number
  }
  render() {
    return (
      const { name } = this.props
    	<div>{name}</div>
    )
  }
}

1.4 组件之间传递JSX

  1. 【针对类组件】,在传递【JSX】的时候,是需要在props里面找到【children】属性里面
import React,{Component} from 'react'

export default class Father extends Component{
   render() {
     return (
    	<div>
      	<ChildenS>
        	<p>向子组件传递JSX</p> 【向子组件传递JSX】
        </ChildenS> 
      </div>
    )
  }
}

export default class ChildenS extends Component{
  render() {
    return {
    	<div>
        { this.porps.children } 【 拿到父组件传递过来的jsx 】
      </div>
    }
  }
}
  1. 【函数组件】,直接通过props的【childre】进行接收
  2. 无论传递多少【JSX】语法,都会被渲染

2. 在React中实现slot(插槽)

  1. 在react当中,没有【slot】的这个说法,只有在【vue里,才会有slot】,那么,要想在react当中实现Vue里面的插槽功能,该如何实现
  2. 在react中,可以通过向子组件传递【JSX】的方式,向子组进行传递
1. 父组件 】
import React from 'react'
import Home from './Home'

function App() {
  const leftSlot = (<div>
    <span>我是左边内容</span>
  </div>)
  const centerSlot = (<div>
    <span>我是中间内容</span>
  </div>)
  const rightSlot = (<div>
    <span>我是右边内容</span>
  </div>)
  return (
    <div>
      <Home leftSlot={leftSlot} centerSlot={centerSlot} rightSlot={rightSlot} />
      <button>父组件按钮</button>
    </div>
  )
  
}

export default App
【 子组件,通过props,拿到父组件传递的JSXfunction Home(props) {
  console.log(props);
  return (
    <div>
      <div>{props.leftSlot}</div>
      <div>{props.centerSlot}</div>
      <div>{props.rightSlot}</div>
    </div>
  )
}

export default Home

3. 跨组件通信

3.1 多层嵌套组件参数传递

3.2 兄弟组件之间的参数传递

  1. 兄弟组件之间参数传递,兄弟组件,都有一个父级组件,组件A向组件B传递参数,组件A通过将参数传递给父组件,父组件在通过【props】传递给【组件B】
【 父组件 】
import React,{Component} from 'react'
import Home from './Home'
import Header from './Header'
class App extends Component {
  state = {
    name: ''
  }
  getParams(i) {
    const name = i.name
    this.setState({
      name
    })
  }
  render() {
    return (
      <div>
        <Header  handole={this.getParams.bind(this)}></Header>
        <Home name={this.state.name}/>
      </div>
    )
  }
  
}

export default App

【 组件A 】
import React,{Component} from 'react'

class Header extends Component {
  constructor(props) {
    super(props);
    this.state = {}
  }
  componentDidMount () {
    this.props.handole({name: '组件A中的数据'})
  }
  render() {
    return (
      <div>组件A</div>
    )
  }
}

export default Header
【 组件B 】
function Home(props) {
  return (
    <div>
      组件B:
      <span>{props.name}</span>
    </div>
  )
}

export default Home
  1. 【通过发布/订阅进行传递】events
1. 下载包 npm install events -S 】
【 2. 引入使用 】
import {EventEmitter} from 'events';
const emitter =  new EventEmitter();

【 发布 】
 emitter.emit('事件名称',传递的参数)
【 侦听 】
const emitter =  new EventEmitter();

emitter.on('事件名',(接收的参数) => {
	逻辑代码
})
2. 用法2 】

【 eventBus.jsimport {EventEmitter} from 'events';
export default new EventEmitter();
【 发布 】
import Bus from './eventBus'
Bus.emit('changeSiblingsData', msg);
【 侦听 】
import Bus from './eventBus'
Bus.addListener('changeSiblingsData', (msg) => {
  this.setState({
    bus: msg,
  });
  console.log(msg);
});

4. 组件状态(类)

  1. 【什么是组件的状态】组件的状态是什么,就是某一个组件自己内部私有的数据,也就是状态。 针对单个组件自己内部的数据,当修改组件内部的数据,界面上Dom中的数据就会自动更新

4.1 函数组件与类组件的区别

对比:】

  1. 方式一:通过 function构造函数 创建组件。内部没有 state 私有数据,只有 一个 props 来接收外界传递过来的数据。
  2. 方式二:通过 class 创建子组件。内部除了有 this.props 这个只读属性之外,还有一个专门用于 存放自己私有数据的 this.state 属性,这个 state 是可读可写的。

基于上面的区别,我们可以为这两种创建组件的方式下定义: 使用 function 创建的组件,叫做【无状态组件】;使用 class 创建的组件,叫做【有状态组件】。

  1. 【本质区别】有状态组件和无状态组件,最本质的区别,就是有无 state 属性。同时, class 创建的组件,有自己的生命周期函数,但是,function 创建的 组件,没有自己的生命周期函数。
  2. 【什么时候去使用有状态组件,什么时候用无状态组件】
    • 如果一个组件需要存放自己的私有数据,或者需要在组件的不同阶段执行不同的业务逻辑,此时,非常适合用 class 创建出来的有状态组件。
    • 如果一个组件,只需要根据外界传递过来的 props,渲染固定的页面结构即可的话,此时,非常适合使用 function 创建出来的无状态组件。(使用无状态组件的小小好处: 由于剔除了组件的生命周期,所以,运行速度会相对快一点点)。

4.2 state

  1. 类组件中,组件私有的状态,在开发时,常常会看见在【constructor】里面定义state,与在外面定义的state,在这里,无论是在外面定义的还是在【构造器】里面定义的state,都是挂载到实例上的,【state】值是一个对象。
import React,{Component} from 'react'

export default class App extends Componet {
	state = {
  	name: '组件私有的数据'
  }
	render () {
  	return {
    	<div>{ this.state.name }</div>
    }
  }
}

export default class App extends Componet {
	constructor () {
  	this.state = {
      name: '组件私有的数据'
    }
  }
	render () {
  	return {
    	<div>{ this.state.name }</div>
    }
  }
}

4.3 更改组件的状态setState

  1. 【setState】是react提供的一个修改组件状态的方法,通过setState去修改state里面的值
import React,{Component} from 'react'
【 修改后的值,会覆盖原来的值,数据就会被更新到界面Dom元素中。 】
export default class App extends Componet {
	state = {
  	name: '组件私有的数据'
  }
	render () {
  	return {
    	<div>
      	{ this.state.name }
        <button onClick={this.handole}>修改数据</button>
      </div>
    }
  }
	handole = () => {
  	this.setState({
    	name: '修改state里面的值'
    })
  }
}
  1. 【setState】方法的设计,是一个异步的,操作完是不能立刻拿到修改后的值,在使用【setState】的时候,提供两种方式,去获取修改后的值
    • 【方式一:使用async/await】
    • 【方式二:使用setState的第二个参数,传入一个函数,通过这个回调函数,拿到更新后的值】,第二个参数类似于promise里面的成功回调函数。
import React,{Component} from 'react'
export default class App extends Componet {
	state = {
  	name: '组件私有的数据'
  }
   render () {
     return {
    	<div>
      	{ this.state.name }
        <button onClick={this.handole}>修改数据</button>
      </div>
    }
  }
	handole = async () => {  【 方式一: async/awaitawait this.setState({
    	name: '修改state里面的值'
    })
    console.log(this.state.name) // 拿到修改后的值,在做其他的操作
  }
  handole =() => {
  	this.setState({
    	name:'修改state里面的值' 
    },() =>{
    	console.log(this.state.name) 【 方式二: setState的第二个参数传入函数,拿到state更改后的值 】
    })
  }
}
  1. 在使用【setState】,除了参数可以传递一个【对象】,也可以传递一个【函数】,函数接收参数,【state】当前组件的状态。
import React,{Component} from 'react'
export default class App extends Componet {
	state = {
  	count: 0
  }
   render() {
     return (
    	<div>
        { this.state.name }
      	<button onClick={ this.handole }>+</button>
      </div>
    )
  }
	handole =()=>{
  	this.setState((state) =>({ 【 state拿到的是当前组件的私有状态 】
    	count: state.count + 1
    }))
  }
}

5. render函数被调用

  1. 当App组件自己内部的状态进行更改【setState去修改状态】,App组件内部,调用的所有的子组件,子组件里面的render都被调用了,【原因:当更改App组件的状态,App组件内部的render函数,就会被重新调用,当重新调用render函数的时候,发现组件内部有其他的组件调用,就会被重新加载】
  2. 【解决方式:类组件不直接去继承Component】,通过去继承【 PureComponent 】,可以解决组件的render函数被重新调用,【弊端, PureComponent 只能实现类组件的render函数不被调用】,但是函数组件还是无法设置
PureComponent 】会给组件自动生成一个shouldComponentUpdate函数,这个函数可以阻止组件的render函数
重新调用,返回值是布尔值,当返回值为false的时候,会阻止组件的render函数被重新调用

import React,{PureComponent} from 'react'

export default class App extends PureComponent {
   render() {
    return (
    	<div></div>
    )
  }
}
  1. 函数组件的优化【高阶组件memo】
【 memo作用:阻止函数组件无缘无故的被重新的调用 】
【 momo的使用 】
import React,{memo} from 'react'

【 memo会返回一个新的函数 】

const MemoHeader = memo(function Header() {
  retrun (
  	<div></div>
  )
})

export default MemoHeader

6. 高阶组件(待学习)