当前位置: 首页 > news >正文

React(三):脚手架解析、组件分类、生命周期、组件通信

脚手架解析 

1.安装脚手架:

npm i create-react-app -g   //全局安装
create-react-app --version  //查看版本号

2.创建项目:

create-react-app 项目名称 

3.目录结构分析:

4.了解PWA

PWA(Progressive Web App,渐进式网页应用)是一种结合了网页和移动应用优点的技术。

特点:

1.可添加至主屏幕,点击图标后挑战至地址栏;

2.通过App Manifest 和 Service Worker实现离线缓存功能,即使没网也可使用一些离线功能

学习网址:渐进式 Web 应用(PWA) | MDN

组件分类

类组件

1.语法

使用ES6的class语法,继承React.Component,包含render()方法和生命周期方法

2.状态管理:通过this.state初始化状态,使用this.setState()更新,支持复杂状态逻辑

3.生命周期:提供完整的生命周期

4.性能优化:通过shouldComponentUpdate或PureComponent优化渲染

5.render函数返回值:

// 1.类组件
class App extends Component {
  constructor () {
    super()
    this.state={
      message:"你好呀"
    }
  }
  render() {
    // const {message} = this.state
    // 1.react元素:通过jsx编写的代码会被编译成React.createElement,所有返回就是一个react元素
    // return <h1>{message}</h1>

    // 2.返回一个数组
    // return ['aaa','bbb','ccc']
    // return [
    //   <h1>元素1</h1>,
    //   <h1>元素2</h1>,
    //   <h1>元素3</h1>
    // ]

    // 3.返回字符串/数字类型
    // return '你好呀'
    // return 123

    // 4.返回布尔类型或者null-界面什么都不展示
    return true
  }
}

函数组件

1.语法

通过普通函数定义,早期只能渲染UI(无内部状态state),Hooks出现后支持完整功能

2.状态管理:通过useState、useReducer等Hooks管理状态,状态更新更简洁

const [count, setCount] = useState(0); // 函数组件状态

3.无生命周期,可通过useEffect模拟生命周期,如组件挂载、更新、卸载等

4.性能优化:使用React.memo缓存组件,避免不必要的渲染

生命周期

常见生命周期

React内部为了告诉我们当前处于哪些阶段,会对我们组件内部实现的某些函数进行回调,这些函数就是生命周期函数

1.挂载阶段:

(1)Constructor():不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数

  • 初始化内部state
  • 为事件绑定实例this

(2)componentDidMount():在组件挂载后(插入 DOM 树中)立即调用

  • 依赖于DOM的操作可以在这里进行;
  • 在此处发送网络请求就最好的地方;

2.更新阶段:componentDidUpdate()

在更新后会被立即调用,首次渲染不会执行此方法

3.卸载阶段:componentWillUnmount() 会在组件卸载及销毁之前直接调用

如下图所示:

不常见生命周期

  1. getDerivedStateFromProps:state 的值在任何时候都依赖于 props时使用;该方法返回一个对象来更新state;
  2. getSnapshotBeforeUpdate:在React更新DOM之前回调的一个函数,可以获取DOM更新前的一些信息(比如说滚动位置)
  3. shouldComponentUpdate:是否渲染该组件

代码示例:

export class HelloWorld extends React.Component {
  // 1.先执行构造方法:constructor
  constructor() {
    console.log("HelloWorld constructor");
    super();
    this.state = {
      message: "HelloWorld 你好呀"
    };
  }

  changeText() {
    this.setState({
      message: "你好呀,小橙子",
    });
  }

  // 2.执行render方法
  render() {
    console.log("HelloWorld render");
    const {message} = this.state;
    return (
      <div>
        {message}
        <button onClick={() => this.changeText()}>修改文本</button>
      </div>
    )
  }

  // 3.组件被渲染到DOM:被挂载到DOM
  componentDidMount() {
    console.log("HelloWorld componentDidMount");
  }

  // 4.组件的DOM被更新完成
  componentDidUpdate(prevProps, prevState,snapshot) {
    console.log("HelloWorld componentDidUpdate",prevProps, prevState,snapshot);
  }

  //5.组件被从DOM中卸载掉:从DOM移除掉
  componentWillUnmount() {
    console.log("HelloWorld componentUnmount");
  }

  // 不常用的生命周期补充
  // 该函数可以觉得render函数要不要重新渲染
  shouldComponentUpdate() {
    return true
  }

  // 获取在React更新DOM前的一些信息
  getSnapshotBeforeUpdate() {
    console.log("HelloWorld getSnapshotBeforeUpdate");
    return {
      scrollPosition:1000
    }
  }
}

组件通信

父子通信

父传子

Main.jsx-父组件

import React, { Component } from 'react'
import MainBanner from './MainBanner'
import MainProductList from './MainProductList'
import axios from 'axios'

export class Main extends Component {
  constructor () {
    super ()
    this.state = {
      banners:[],
      productList:[]
    }
  }

  componentDidMount () {
    axios.get("http://123.207.32.32:8000/home/multidata").then(
      res => {
        const banners = res.data.data.banner.list
        const recommend = res.data.data.recommend.list
        this.setState({
          banners,
          productList:recommend
        })
      }
    )
  }
  render() {
    const {banners,productList} = this.state
    return (
      <div className='main'>
        <MainBanner banners={banners} title='轮播图' />
        <MainBanner/>
        <MainProductList productList={productList} />
      </div>
    )
  }
}

export default Main

MainProductList.jsx-子组件

import React, { Component } from 'react'

export class MainProductList extends Component {
  // 这部分可以不写→它内部也会将props保存到里面去
  /* constructor (props) {
    super (props)
    console.log(props);
  } */
  render() {
    const {productList} = this.props
    return (
      <div>
        <h1>商品列表</h1>
        <ul>
          {productList.map((item,index)=>{
        return <li key={item.acm}>{item.title}</li>
      })}
        </ul>
      </div>
    )
  }
}

export default MainProductList

注意:props接收数据类型限制和默认值!!!

MainBanner.jsx-子组件

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export class MainBanner extends Component {
  // ES2022开始,可在此处写默认值
  static defaultProps = {
    banners:[],
    title:'默认标题'
  }
  render() {
    const {banners, title} = this.props
    return (
      <div className='banner'>
        <h1>{title}</h1>
        <ul>
          {banners.map((item,index)=>{
        return <li key={item.acm}>{item.title}</li>
      })}
        </ul>
      </div>
    )
  }
}

MainBanner.propTypes = {
  banners:PropTypes.array.isRequired,
  title:PropTypes.string
}

// MainBanner.defaultProps = {
//   banners:[],
//   title:'默认标题'
// }
export default MainBanner
子传父

App.jsx-父组件

import React, { Component } from 'react'
import AddCount from './AddCount'
import SubCount from './SubCount'

export class App extends Component {
  constructor () {
    super()
    this.state={
      count:100
    }
  }

  changeCount(n) {
    this.setState({
      count:this.state.count + n
    })
  }
  render() {
    const {count} = this.state
    return (
      <div>
        <h2>当前计数:{count}</h2>
        <AddCount addClick={(n) => this.changeCount(n)}/>
        <SubCount subClick={(n) => this.changeCount(n)}/>
      </div>
    )
  }
}

export default App

AddCount.jsx-子组件 

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export class AddCount extends Component {
  addCount(n) {
    const click = this.props.addClick
    click(n)
  }
  render() {
    return (
      <div>
        <button onClick={e => this.addCount(1)}>+1</button>
        <button onClick={e => this.addCount(5)}>+5</button>
        <button onClick={e => this.addCount(10)}>+10</button>
      </div>
    )
  }
}

AddCount.propTypes = {
  addCount: PropTypes.func.isRequired
}

export default AddCount

SubCount.jsx-子组件 

import React, { Component } from 'react'

export class SubCount extends Component {
  SubCount(n) {
    this.props.subClick(n)
  }
  render() {
    return (
      <div>
        <button onClick={e => this.SubCount(-1)}>-1</button>
        <button onClick={e => this.SubCount(-5)}>-5</button>
        <button onClick={e => this.SubCount(-10)}>-10</button>
      </div>
    )
  }
}

export default SubCount
插槽用法

1.通过props.children实现插槽

2.通过props直接实现插槽

App.jsx

export class App extends Component {
  render() {
    return (
      <div>
        {/* 1.使用children实现插槽 */}
        <NavBar>
          {/* 
          注意:
          当这里放多个时——children:[button,h2,i] 
          当这里只有一个时,children:button——children就是该元素
          弊端:通过索引值获取传入的元素很容易出错,不能精准的获取传入的原生
          */}
          <button>按钮</button>
          <h2>我是标题</h2>
          <i>斜体文字</i>
        </NavBar>

        {/* 2.使用props实现插槽-父传子-推荐使用该方案 */}
        <NavBarr
          leftSlot={<button>按钮</button>}
          centerSlot={<h2>我是标题</h2>}
          rightSlot={<i>斜体文字</i>}
          >
        </NavBarr>
      </div>
    )
  }
}

 NavBar.jsx

import React, { Component } from 'react'

export class NavBar extends Component {
  render() {
    const {children} = this.props
    return (
      <div className='nav-bar'>
        <div className="left">{children[0]}</div>
        <div className="center">{children[1]}</div>
        <div className="right">{children[2]}</div>
      </div>
    )
  }
}

export default NavBar

 NavBarr.jsx

import React, { Component } from 'react'

export class NavBarr extends Component {
  render() {
    const {leftSlot,centerSlot,rightSlot} = this.props
    return (
      <div className='nav-bar'>
        <div className="left">{leftSlot}</div>
        <div className="center">{centerSlot}</div>
        <div className="right">{rightSlot}</div>
      </div>
    )
  }
}

export default NavBarr
综合案例

App.jsx 

import React, { Component } from 'react';
import { TabControl } from './TabControl.jsx';
import './style.css'

export class App extends Component {
  constructor() {
    super();

    this.state = {
      titles: ["流行", "新款", "精选"],
      activeIndex:0
    };
  }

  changeTab(index) {
    this.setState({
      activeIndex:index
    })
  }

  render() {
    const { titles,activeIndex } = this.state;
    return (
      <div>
        <TabControl titles={titles} activeClick={index => this.changeTab(index)} />
        <div className='tab-content'>{titles[activeIndex]}</div>
      </div>
    );
  }
}

export default App;

TabControl.jsx 

import React, { Component } from 'react'

export class TabControl extends Component {
  constructor () {
    super()
    this.state = {
      tabActive:0
    }
  }
  tabActive(index) {
    this.setState({
      tabActive:index
    })
    this.props.activeClick(index)
  }
  render() {
    const { titles } = this.props;
    const { tabActive } = this.state;
    return (
      <div className='tab-control'>
        {titles.map((item,index) => 
        <div 
          key={index}
          className={tabActive === index ? 'active' : ''}
          onClick={() => this.tabActive(index)}
          >{item}</div>)}
      </div>
    )
  }
}

export default TabControl

非父子通信

Context API

通过 Provider 和 Consumer 跨层级共享数据,避免逐层传递

uesr-context.js

import React from "react";  

// 1.创建一个Context
const UserContext = React.createContext()

export default UserContext

App.jsx 

import React, { Component } from 'react'
import Home from './Home'
import UserContext from './context/uesr-context';

export class App extends Component {
  constructor() {
    super();

    this.state = {
      info:{name:'why',age:18}
    }
  }
  render() {
    const {info} = this.state;
    return (
      <div>
        <h2>App</h2>
        {/* 1.给Home传递数据 */}
        {/* <Home name="why" age={18}/> */}
        {/* <Home name={info.name} age={info.age}/> */}
        {/* 官方文档:若已经有一个props对象,可使用展开运算符传递整个props对象 */}
        {/* <Home {...info}/> */}

        {/* 2.普通的Home */}
        {/* 第二步:通过Context中的Provider中的value属性为后代提供数据 */}
        <UserContext.Provider value={{name:'why',age:18}}>
          <Home {...info}/>
        </UserContext.Provider> 
      </div>
    )
  }
}

export default App

HomeInfo.jsx-孙组件 

import React, { Component } from 'react'
import InfoContext from './context/theme-context'
import UserContext from './context/uesr-context';

export class HomeInfo extends Component {
  render() {
    // 第四步:获取数据,并使用
    console.log(this.context);
    return (
      <div>
        <h1>HomeInfo</h1>
        <p>ThemeContext:{this.context.color}</p>
        <p>{this.context.size}</p>
        <UserContext.Consumer>
          {(value)=><p>{value.name}</p>}
        </UserContext.Consumer>
      </div>
    )
  }
}

// 第三步:设置组件contextType的类型为某一个Context
HomeInfo.contextType = InfoContext

export default HomeInfo
EventBus事件总线

event-bus.js

import  {HYEventBus} from "hy-event-store"

// 1.创建一个都能访问的事件总线
const eventBus = new HYEventBus()

export default eventBus

App.jsx 

import React, { Component } from 'react'
import Home from './Home'
import eventBus from './utils/event-bus'

export class App extends Component {
  constructor() {
    super();

    this.state = {
      name: 'why',
      age: 18,
      gender: '男',
      info:{bookname:'jsbook',price:100}
    }
  }
  componentDidMount () {
    // eventBus.on('bannerPrev', (name,age,gender) => {
    //   console.log(name,age,gender);
    //   this.setState({name,age,gender})
    // })

    //第三个参数:传递给回调函数的上下文(即This对象),用于指定函数内部的this指向
    eventBus.on('bannerPrev', this.bannerPrevClick,this)
    eventBus.on('bannerNext', this.bannerNextClick,this)
  }

  // 将其定义成一个函数,抽取出去
  bannerPrevClick(name,age,gender) {
    this.setState({name,age,gender})
  }

  bannerNextClick(info) {
    this.setState({
      info:{...info}
    })
  }
  // 当该子组件销毁时,需要移除监听事件
  componentWillUnmount () {
    eventBus.off('bannerPrev',this.bannerPrevClick)
  }
  render() {
    const {name,age,gender,info} = this.state;
    return (
      <div>App-{name}-{age}-{gender}
        <Home />
        <h2>{info.bookname}-{info.price} </h2>
      </div>
    )
  }
}

export default App

HomeBanner.jsx-孙组件 

import React, { Component } from 'react'
import eventBus from './utils/event-bus';

export class HomeBanner extends Component {
  prevClick() {
    console.log('上一个');
    eventBus.emit('bannerPrev',"Lily",22,"女")
  }
  nextClick() {
    console.log('下一个');
    eventBus.emit('bannerNext',{bookname:'《算法导论》',price:99})
  }
  render() {
    return (
      <div>
        HomeBanner
        <button onClick={e => this.prevClick()}>上一个</button>
        <button onClick={e => this.nextClick()}>下一个</button>
      </div>
    )
  }
}

export default HomeBanner

相关文章:

  • Rust + WebAssembly 实现康威生命游戏并进行单元测试
  • 从中序与后序遍历序列构造二叉树(Java)
  • Rockchip --- 图像时延优化
  • pandas中excel自定义单元格颜色
  • 3D视觉相机引导机器人的原理
  • Arduino入门常用指令详解及语法指南
  • DeepSeek在数据爬取领域的革新实践:架构解析与实战指南
  • 案例驱动的 IT 团队管理:创新与突破之路:第三章 项目攻坚:从流程优化到敏捷破局-3.2.2 Scrum vs Kanban 的场景化选择
  • DeepSeek:AI 搜索引擎的革新者?
  • Chat2DB:自然语言生成 SQL 的时代来临,数据库管理更简单
  • FPGA中级项目4——DDS实现
  • 物联网中设备与平台通信的方式
  • SENT接口
  • 如何处理和格式化日期差异:JavaScript 日期差异计算实例
  • 高项第十二章——项目质量管理
  • 关于foobar2000插件的一点理解
  • ollama 可以通过127.0.0.1访问,但是无法通过本机ip访问
  • 人工智能领域大模型、大模型使用、AI工作流 学习路径
  • Oracle ASM 磁盘组冗余策略
  • 向量数据库:AI时代的“新基建”
  • 有哪些网站可以做设计挣钱/sem广告
  • 移动网站优化 sousuoyouhua.com/东莞seo排名公司
  • 免费个人业务网站制作/seo做得比较好的企业案例
  • wordpress 地址设置方法/搜索引擎快速优化排名
  • 酷家乐网站做墙裙教程/网络营销策划书1500字
  • 应聘ui设计师自我介绍/seo数据