组件化开发

将一个页面拆分成一个个小的功能块
组件的定义方式:**函数式组件****类组件**
组件内部是否有状态需要维护: **无状态组件****有状态组件**
组件职责:展示型组件和容器型组件

组件类型

类组件的定义要求:

  • 必须以大写字符开头
  • 继承自**React.Component**
  • 必须实现**render**函数,是唯一必须实现的方法

    1. import React, { Component } from "react";
    2. class App extends Component {
    3. render() {
    4. return (
    5. <div></div>
    6. )
    7. }
    8. }
    9. export default App;

    函数式组件的要求:返回值和类组件中render函数返回一样的内容。

  • 函数式组件没有生命周期

  • 没有内部状态state
  • 没有this指向组件实例

    1. function App() {
    2. return (
    3. <div>
    4. App
    5. </div>
    6. )
    7. }

    组件生命周期

    组件从创建到销毁的整个过程,称之为组件的生命周期。

  • constructor: 初始化state或者绑定this实例

  • render: 渲染JSX
  • 挂载阶段:**componentDidmount**
    • DOM操作
    • 网络请求
    • 添加订阅
  • 更新阶段: **componentDidUpdate**
  • 卸载阶段: **componentWillUnmount**

image.png

image.png

  1. getSnapshotBeforeUpdate() {
  2. return {}
  3. }
  4. componentDidUpdate(prevProps,prevState,snapshot) {
  5. }

组件通信

父传子

父组件通过**属性=值**的形式来传递给子组件数据
子组件通过**props**参数获取父组件传递的数据

  1. <ChildCpns attr1={attr1} title="title标题"/>
  2. const { attr1, title } = this.props
  1. <Component {...obj}/>

参数propTypes

  1. import PropTypes from 'prop-types'
  2. ChildCpn.propTypes = {
  3. name: PropTypes.string.isRequired,
  4. }
  5. // 指定props的默认值
  6. ChildCpn.defaultProps = {
  7. name: 'zzf'
  8. }
  9. class ChildCpn extends Component {
  10. static defaultProps = {
  11. name: 'zzf'
  12. }
  13. }

子传父

父组件给子组件传递一个回调函数,在子组件中调用这个函数。

  1. import React, { Component } from 'react'
  2. import AddCounter from './AddCounter'
  3. export class App extends Component {
  4. constructor() {
  5. super()
  6. this.state = {
  7. counter: 100
  8. }
  9. }
  10. changeCounter(count) {
  11. this.setState({ counter: this.state.counter + count })
  12. }
  13. render() {
  14. const { counter } = this.state
  15. return (
  16. <div>
  17. <h2>当前计数: {counter}</h2>
  18. <AddCounter addClick={(count) => this.changeCounter(count)}/>
  19. </div>
  20. )
  21. }
  22. }
  23. export default App
  1. import React, { Component } from 'react'
  2. // import PropTypes from "prop-types"
  3. export class AddCounter extends Component {
  4. addCount(count) {
  5. this.props.addClick(count)
  6. }
  7. render() {
  8. return (
  9. <div>
  10. <button onClick={e => this.addCount(1)}>+1</button>
  11. <button onClick={e => this.addCount(5)}>+5</button>
  12. <button onClick={e => this.addCount(10)}>+10</button>
  13. </div>
  14. )
  15. }
  16. }
  17. // AddCounter.propTypes = {
  18. // addClick: PropTypes.func
  19. // }
  20. export default AddCounter

组件插槽

  • 组件的children子元素
  • 通过props传递 ```jsx

    left

    center

    right

    </Navbar

const { children } = this.props // 当传递多个元素时,children是一个数组 // 当传递一个元素时,children就是传递的元素

  1. ```jsx
  2. <Navbar
  3. leftSlot={<h2>left</h2>}
  4. centerSlot={<h2>center</h2></h2>}
  5. rightSlot={<h2>right</h2>}
  6. />

作用域插槽

通过回调函数返回内容来实现

  1. <TabControl itemType={item => <button>{item}</button>} />
  2. // TabControl.js,把该组件的item通过props传递给父组件
  3. {this.props.itemType(item)}

非父子组件通信

Context

**Context**提供了一种在组件之间共享值的方式,而不必逐层传递props

  1. // 1. 创建一个context,并提供defaultValue
  2. const ThemeContext = React.createContext({})
  1. // 2. 通过Provider的value属性为后代组件传递数据
  2. <ThemeContext.Provider value={{color: 'red'}}>
  3. <Home/>
  4. </ThemeContext.Provider>
  5. // 3. 设置后代组件的contextType
  6. HomeInfo.contextType = ThemeContext
  7. // 4. 使用数据
  8. console.log(this.context.color)
  1. // 当为函数式组件或需要使用多个context时使用Context.Consumer
  2. <ThemeContext.Consumer>
  3. {
  4. value => {
  5. return <h2>{value.color}</h2>
  6. }
  7. }
  8. </ThemeContext>

事件总线

  1. // 引入自建库里封装的eventbus
  2. import { HYEventBus } from 'hy-event-store'
  3. const eventBus = new HYEventBus()
  4. export default eventBus
  5. eventBus.emit('eventName','zzf')
  6. eventBus.on('eventName',cb)
  7. eventBus.off('eventName',cb)

setState使用

必须通过**setState**来告知React数据发生了变化

合并state

  1. state = {
  2. name: 'zzf',
  3. age: 21
  4. }
  5. this.setState({
  6. name: 'zdf'
  7. })
  8. Object.assign({name: 'zzf', age: 21}, { name: 'zdf'})

传入回调函数

  1. this.setState((state, props) => {
  2. // 1. 可以在回调函数中编写新的state的逻辑
  3. // 2. 当前的回调函数会将之前的state和props传递进来
  4. console.log(this.state.name} // 打印原来的state
  5. // 返回state
  6. return {
  7. name: 'zdf'
  8. }
  9. })

异步调用

setState在React的事件处理中是一个异步调用 :::info setState设计为异步,可以显著的提升性能;

  1. 如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的;最好的办法应该是获取到多个更新,之后进行批量更新;
  2. 如果同步更新了state,但是还没有执行render函数,那么state和props(state中的数据传递给子组件的props)不能保持同步;state和props不能保持一致性,会在开发中产生很多的问题; ::: ```javascript this.setState({name: ‘zdf’}) console.log(this.state.name) // ‘zzf’

// 可以在setState中传入第二个参数:获取合并(更新)后的数据 this.setState({ name: ‘zdf’ },() => { console.log(this.state.name) // ‘zdf’ })

this.setState((state) => { return { counter: state.counter + 1 // 这里获取的也是更新后的数据 } })

  1. <a name="zHFXQ"></a>
  2. #### 同步的setState
  3. React18之前,`setTimeout\Promise.then\原生DOM事件`是同步的
  4. ```javascript
  5. // React18之前,获取的是同步结果
  6. // React18之后,进行批处理,仍然是异步的
  7. increment() {
  8. setTimeout(() => {
  9. this.setState({
  10. counter: this.state.counter + 1
  11. })
  12. console.log(this.state.counter) // + 1后的结果
  13. },0)
  14. }
  1. import { flushSync } from 'react-dom';
  2. changeText() {
  3. setTimeout(() => {
  4. flushSync(() => {
  5. this.setState({message: '要好好生活'})
  6. })
  7. console.log(this.state.message)
  8. },0)
  9. }