React类组件实战知识点

一、React介绍

React这个项目是facebook开源出来的一个优秀前端库。

官网将React称为库(jquery库),React默认提供的只是前端的页面更新方案。并没有提供完整的前端所有核心内容。比如路由、状态机。这些都需要我们自己搭建。

在2013年5月开源出来。

提出了很多概念在当时都是比较前卫的思想。

组件化开发、虚拟dom加载。

React开发有两个分支:

  1. React-DOM:这是一个包。这个分支解决我们React开发WEB业务
  2. React-Native:专门用于开发移动端。只能开发android和ios

学习:react.nodejs.cn/

优势:

  1. 组件化开发、虚拟dom设计,对应项目来说性能有保证。
  2. 技术成熟、社区配置完善。适用于中大型web项目
  3. facebook团队维护,版本更新、担心后续维护
  4. 目前来说React在公司里面,适用于中大型项目。大公司用的比较多

二、搭建项目

目前React官方基于webpack封装了脚手架。

你们可以基于脚手架工具。快速搭建一个项目。

(1)创建项目

js 复制代码
npx create-react-app 项目名字

脚手架工具的文档:

cra.nodejs.cn/docs/gettin...

这种方式来创建项目,本地无需安装脚手架。就可以直接创建项目

临时在本地安装一个脚手架工具,创建项目,创建完成后,删除脚手架

在本地安装好脚手架,直接用本地脚手架搭建项目。这种方式我们不推荐。

js 复制代码
npm i create-react-app -g
create-react-app 项目名字

创建项目,项目名字不能出现大写字母,不能中文

(2)启动项目

安装yarn包管理器。默认提示你采用yarn启动项目

js 复制代码
yarn start

npm来启动项目

js 复制代码
npm run start

(3)改造项目

src目录下面只留

  1. index.js:这个文件项目启动文件。类似于Vue中main.js以后需要全局挂载内容都可以在里面写
  2. App.js:后缀是js,其中这个是React组件。里面代码不是原生js代码。会在index默认加载App.js文件

剩下的文件夹需要大家自己创建

component:存放组件

utils:存放开发自己封装工具

assets:静态资源目录

libs:存放第三方包 sdk

apis:存放异步请求

三、组件的介绍

(1)概念介绍

组件化思想:针对页面UI部分,进行了页面的拆分。将公共的页面模板拆分,最后在自己组合在一起。

模块化思想:针对JS脚本设计,将代码进行模块拆分。减少当前脚本体积,提高复用性。比如很多的工具提取出来。

html 复制代码
<html>
<head>
    <script src="./a.js"></script>
    <script src="./b.js"></script>
</head>
</html>

a.js

js 复制代码
var m = 10

b.js

js 复制代码
var m = 20

最早解决思路

采用IIFE来解决问题。

a.js

js 复制代码
(function(){
    var m = 10
})()

b.js

js 复制代码
(function(){
    var m = 20
})()

requirejs seajs等等。第三方开发的模块中代码。

ES6这个版本,官方也推出了模块化思想

js 复制代码
import xxx from "./"
export {}
export default {}

工程化思想:以前我们项目在没有脚手架搭建的时候,都是自己下载包。自己来进行代码压缩混淆。

基于webpack或者vite这种打包工具。你们直接依赖这些工具完成前端项目构建,打包、部署

(2)组件创建

React中组件有两种

  1. 类组件开发:采用的面向对象开发思想。
  2. 函数组件开发:基于函数来设计组件。所有业务都是基于函数来开展

在目前项目开发过程中,两种组件都会接触到。

在React中我们组件后缀有两种格式,js在里面写jsx代码,jsx后缀支持的一种格式

安装插件

创建组件的时候

rcc:创建一个类组件的模板

rfc:创建一个函数组件的的模块

类组件的代码

js 复制代码
import React, { Component } from 'react'

/**
 * 在React中当写一个类,继承Component,当前这个类是一个组件
 * Component:React提供的组件
 */
class Header extends Component {
    /**
     * render渲染函数,我们当前这个组件要显示模板,在render里面
     * JSX代码
     * @returns 
     */
    render() {
        return (
            <div>Header</div>
        )
    }
}


export default Header

React设计思想:将JS+HTML模板统一放在一个文件中,写完了代码后。JS 和HTML分开解析。最后html+css+js

原生JS代码

js 复制代码
const array = [1,2,3]
let temp = ""
for(var i=0;i<array.length;i++){
    temp += `<li>${array[i]}</li>`
}
obody.innerHTML = temp

函数组件的代码

js 复制代码
import React from 'react'

function Content() {
    return (
        <div>Content</div>
    )
}


export default Content

我们先学习类组件,后面再学习函数组件

(3)JSX概念

组件本身设计的一种模块,可以将代码抽取出来。再React中就是采用JSX来作为组件的语法

JSX = JavaScript + XML

JavaScript就是基于ES5\ES6引入过来规范。

XML:标签语言,表示组件中所有标签模板。代码解析的时候。XML标签就是转化HTML标记了。

接下来你再组件中写的所有代码都是JSX代码。浏览器无法直接解析。

在启动项目的时候

sql 复制代码
yarn start

webpack就会开始打包项目。babel这个插件,将jsx代码转化为浏览器能是被的js代码。最后才浏览器运行

四、组件的页面

(1)组件中类名

js 复制代码
<div className="">

每个组件都要求,必须提供根标签。否则报错

js 复制代码
<React.Fragment></React.Fragment>

使用这个来作为根标签,最终页面不会渲染出来。解决报错问题

js 复制代码
<></>

也是代表虚拟标签,最终并不会解析出来。

(2)组件中引入图片

jsx 复制代码
import React, { Component } from 'react'
import logo from "../assets/images/logo-250px.645f24b5.png"
const logo2 = require("../assets/images/logo-250px.645f24b5.png")

/**
 * 在React中当写一个类,继承Component,当前这个类是一个组件
 * Component:React提供的组件
 */
class Header extends Component {
    /**
     * render渲染函数,我们当前这个组件要显示模板,在render里面
     * JSX代码
     * render函数:方法重写,调用render优先执行子类render
     * @returns 
     */
    render() {
        return (
            <React.Fragment>

                <div className='container'>
                    <div className='logo'>
                        <img src={logo2} alt="" />
                    </div>
                    <div>
                        <span>欢迎【xx】登录</span>
                    </div>
                </div>
                <p></p>

            </React.Fragment>
        )
    }
}


export default Header

总结:

  1. 服务器网络图片,地址直接写死,动态加载都可以。
  2. 图片本地图片,采用import 或者 require的方式引入进来,动态设置src属性

在JSX中动态绑定一个属性,我们需要src={logo}

(3)样式设计

在JSX中支持两种样式设计

  1. 内联样式:直接在标签上面style属性。
  2. 外部样式:将样式文件提取到外部css文件中,引入进来使用

内联样式:JSX中的样式已经经过封装过了。

js 复制代码
<span style={{color:"red",fontSize:"20px"}}>欢迎【xx】登录</span>

因为style对象可以接受多个css样式,所以我们提供的是一个对象,定义很多样式

外部样式:

在styles文件夹下面创建css文件header.css

js 复制代码
.container{
    display: flex;
    justify-content: space-between;
    height: 80px;
    width: 100%;
    box-shadow: 0px 0px 10px rgba(0,0,0,.3);
    padding: 0px 15px;
    box-sizing: border-box;
}
.container .logo{
    width: 180px;
    height: 60px;
    border: 1px solid red;
    margin-top: 10px;
}
.container .logo img{
    width: 100%;
}

JSX代码

jsx 复制代码
render() {
        return (
            <React.Fragment>
                <div className='container'>
                    <div className='logo'>
                        <img src={logo2} alt="" />
                    </div>
                    <div>
                        <span>欢迎【xx】登录</span>
                    </div>
                </div>
                <p></p>

            </React.Fragment>
        )
    }

(4)样式模块化

在组件中样式导入过后默认是全局的样式,

不管在哪个组件中,一旦名字一致,相互影响

为了解决这个问题,React提出了一个概念。模块化样式。

开发步骤 styles文件夹下面创建文件名字 tabA.module.css

js 复制代码
.op{
    color: pink;
}
.box span{
    color:blue
}

组件中使用

js 复制代码
import styles from "../assets/tabA.module.css"

<div clasName={styles.box}></div>

(5) 组件的内部数据

Vue中组件内部数据使用data来定义。只要放在data中,这个数据响应式变化。一旦发生数据更新,页面也会更新

在React中组件内部数据采用state来定义

语法一:在构造器中定义state变量

js 复制代码
constructor(){
        //只要有继承,构造器第一句话必须写super
        //super代表调用父类构造器,先创建父类
        //当你不写构造器的时候,编辑器默认添加constructor
        super()
        //state对象里面存放的数据,类似于Vuedata定义的数据
        this.state = {
            msg:"小王"
        }
    }

页面上使用这个变量的时候

jsx 复制代码
<div>{this.state.msg}</div>

为了更加优化代码,一般会在render函数里面解构,在去使用

jsx 复制代码
render() {
        const {msg} = this.state
        return (
            <React.Fragment>
                <div className='menu'>
                    <ul>
                        <li className='active'>拼团管理</li>
                        <li>拼团数据</li>
                        <li>扩展内容</li>
                    </ul>
                </div>
                <div className='main'>
                    <p>{msg}</p>
                    <TabA></TabA>
                    <TabB></TabB>
                </div>
            </React.Fragment>
        )
    }

语法二:无需再构造器中定义

jsx 复制代码
import React, { Component } from 'react'
import "../assets/styles/content.css"
import TabA from './TabA'
import TabB from './TabB'
import TabC from './TabC'

export default class Content extends Component {
    state = {
        msg:"xiaofeifei"
    }
    render() {
        const {msg} = this.state
        return (
            <React.Fragment>
                <div className='menu'>
                    <ul>
                        <li className='active'>拼团管理</li>
                        <li>拼团数据</li>
                        <li>扩展内容</li>
                    </ul>
                </div>
                <div className='main'>
                    <p>{msg}</p>
                    <TabA></TabA>
                    <TabB></TabB>
                </div>
            </React.Fragment>
        )
    }
}

(6)组件事件绑定

基本语法

React标签上面事件都被封装了一次。JSX的规则

命名规范采用驼峰命名方式

Vue

vue 复制代码
<div @click="check">
    
</div>
js 复制代码
check(){
    
}
<div onClick={this.check}>
<div onChange={this.check}>
事件参数传递
js 复制代码
 <li onClick={this.check} className='active'>拼团管理</li>

绑定事件,提供事件函数,如果这个函数没有加括号。将函数地址放在这个模板中。

点击li元素,找到这个函数,并执行

js 复制代码
 <li onClick={this.check(123)} className='active'>拼团管理</li>

对于JSX来说,立即调用check这个函数。

解决这个问题方案:

js 复制代码
 <li onClick={()=>this.check("TabA")} className='active'>拼团管理</li>

给onClick这个事件绑定一个箭头函数,再这个箭头函数里面调用check函数。

只有事件被触发,箭头函数调用,check才会执行。传递参数过去

事件绑定中this指向问题

如果你函数是普通函数,默认拿到this结果为undefined

页面中绑定事件,节点来触发事件。React底层处理过,并不会直接返回节点。

js 复制代码
import React, { Component } from 'react'
import styles from "../assets/styles/tabA.module.css"

export default class TabA extends Component {
  state = {
    count: 10
  }

  changeCount() {
    //React底层处理过这个事件
    console.log(this); //TabA
    this.setState({
      count:20
    })

  }
  render() {
    const { count } = this.state
    return (
      <div>
        <p>state的数据:{count}</p>
        <button onClick={this.changeCount.bind(this)}>点击触发</button>
      </div>
    )
  }
}

如果函数是普通函数,绑定事件bind来改变this的指向,函数里面this指向当前组件。

js 复制代码
import React, { Component } from 'react'
import styles from "../assets/styles/tabA.module.css"

export default class TabA extends Component {
  state = {
    count: 10
  }
  changeCount = ()=>{
    console.log(this); //TabA
  }
  
  render() {
    const { count } = this.state
    return (
      <div>
        <p>state的数据:{count}</p>
        <button onClick={this.changeCount}>点击触发</button>
      </div>
    )
  }
}

总结:以后再React开发过程中,能用箭头函数就直接用箭头函数。

事件对象

只要有事件绑定,那一定会有event事件对象产生。

默认绑定不传递参数的时候,事件函数第一个参数就是event对象

js 复制代码
changeCount = (event)=>{
    console.log(event.target);
}
<button onClick = {this.changeCount}>

如果你的事件函数要传递参数

js 复制代码
changeCount = (event,val)=>{
    console.log(event,val);
  }
<button onClick={(event)=>this.changeCount(event,123)}>点击触发</button>

遇到要传递参数的时候,外层箭头函数才是事件绑定的函数。传递给内部的函数使用

(7)组件数据更新

组件内部数据state来定义。直接修改state的值,页面并不会检测更新

js 复制代码
check(val){
        this.setState({
            active:val
        })
    }

当React检测到调用这个函数,将原来值进行替换。render函数重新调用一次

当我们进行数据修改的时候,setState里面传递的新的对象,修改的值和会原来state的值进行合并。不会覆盖

更新规则

setState在更新数据的时候。合并操作。并不是覆盖操作

js 复制代码
state = {
    count: 10,
    msg:"xiaowang"
  }
  changeCount = (event,val)=>{
    this.setState({
      count:val
    })
  }

修改的时候,指定了修改的变量。state原来的值没有变化,并不会被覆盖。

异步更新

setState这个函数,可以更新state的数据,并且调用render实现页面刷新。

但是本身是一个异步任务。无法立即获取修改的结果

js 复制代码
changeCount = (event,val)=>{
    //异步代码。
    this.setState({
      count:val
    })
    
    console.log(this.state.count); //原来的数据
  }

为了性能考虑,设计的方案,不管是Vue还是React,我们都是异步更新

为了能获取更新过后的结果,我们提供回调函数

js 复制代码
changeCount = (event, val) => {
    //异步代码。
    this.setState({
      count: val
    }, () => {
      console.log(this.state.count); //得到更新过后结果
    })
  }
合并更新

setState一旦调用,数据发生更新,render就会被执行

js 复制代码
changeCount = (event, val) => {
    //异步代码。
    this.setState({
      count: val
    })
    this.setState({
      count: 1000
    })
  }

一次性执行多个二setState,每个更新任务,都会放在队列中。

队列中任务会取出来进行合并,当发现多个任务的时候。后进入队列中将内容和前面任务合并了。

合并过后,只需要更新页面一次。不要频繁的触发

五、组件通信

父子组件通信

  1. 父传子:在父组件中定义值,通过动态参数传递给子组件,子组件接受结果
  2. 子传父:子组件调用父组件传递过来函数,将内容传递回去

父传子

组件的数据来源于两部分:

  1. 组件内部自身状态,state来定义
  2. 组件外部的状态,props接受

父组件

js 复制代码
state = {
    currentPage:1,
    pageSize:3
}

const {currentPage,pageSize} = this.state
<Pageing currentPage={currentPage} pageSize={pageSize}>

子组件

如果是类组件,默认在this对象身上会有一个props的对象,这个对象默认代表外部数据。如果你传递。空对象

js 复制代码
import React, { Component } from 'react'
import style from "../../assets/styles/fenye.module.css"

export default class Pageing extends Component {
    //mounted 挂载完毕
    componentDidMount(){
        console.log(this.props); -->{currentPage:1,pageSize:3}
    }
    render() {
        return (
            <div className={style.fenye}>
            	
            </div>
        )
    }
}

将props的值取出来,页面渲染。父组件中变量产生数据变化,子组件跟着变化

子组件如果要针对props的数据进行处理,一般是在子组件计算属性。

不要出现将props的值赋值给state,然后再去用state

验证

父组件调用子组件的时候,如果遇到子组件针对参数有验证规则。我们严格按照子组件要求来传递参数

子组件可以添加验证规则,一旦验证规则生效。使用这个组件,可以根据规则来传递参数

数据类型校验

在React老版本中,需要我们自己下载验证包,但是在新版本里面无需下载,直接使用

js 复制代码
import React, { Component } from 'react'
import style from "../../assets/styles/fenye.module.css"
import PropTypes from "prop-types"

class Pageing extends Component {
    //mounted 挂载完毕
    get totalPage() {
        const { pageSize, total } = this.props
        return Math.ceil(total / pageSize)
    }
    render() {
        const { currentPage, pageSize, total } = this.props
        return (
            <div className={style.fenye}>
               ....
            </div>
        )
    }
}

//在暴露给外部调用之前,我们内部先进行props的验证
Pageing.propTypes = {
    currentPage:PropTypes.number.isRequired,
    pageSize:PropTypes.number.isRequired
}

export default Pageing

指定接受的参数要进行验证。

也可以设置默认值

js 复制代码
import React, { Component } from 'react'
import style from "../../assets/styles/fenye.module.css"
import PropTypes from "prop-types"

class Pageing extends Component {
    //mounted 挂载完毕
    get totalPage() {
        const { pageSize, total } = this.props
        return Math.ceil(total / pageSize)
    }
    render() {
        const { currentPage, pageSize, total } = this.props
        return (
            <div className={style.fenye}>
                ....
            </div>
        )
    }
}

Pageing.defaultProps = {
    currentPage:3
}

export default Pageing

如果你没有传递对应属性,采用默认props值。父组件传递了属性。以父组件的数据为主

子传父

父组件传递一个函数给子组件,子组件调用这个函数。就可以将参数传递过去

父组件

js 复制代码
getCurrentPage = (page)=>{
    
}

<Pageing getCurrentPage={this.getCurrentPage}>

子组件调用

js 复制代码
fenye = (event)=>{
    const page = event.target.getAttribute("index")
    console.log(typeof page) //string
    this.props.getCurrentPage(Number(page))
}
<span index={1} onClick = {this.fenye}>1</span>

六、组件封装插槽

在React中子组件需要接受父组件定义模板。可以用插槽来接受数据

js 复制代码
import React, { Component } from 'react'
import style from "../../assets/styles/model.module.css"

export default class Model extends Component {
  render() {
    return (
      <div className={style.container}>
        <div className={style.modelContent}>
            <h3>添加优惠券</h3>
            {this.props.children}
            {/* 存放表单 */}
            <button>关闭</button>
        </div>
      </div>
    )
  }
}

this.props.children代表接受父组件传递过来模板

相关推荐
whysqwhw19 分钟前
js之Promise
前端
恋猫de小郭4 小时前
Flutter 3.35 发布,快来看看有什么更新吧
android·前端·flutter
chinahcp20085 小时前
CSS保持元素宽高比,固定元素宽高比
前端·css·html·css3·html5
gnip6 小时前
浏览器跨标签页通信方案详解
前端·javascript
gnip6 小时前
运行时模块批量导入
前端·javascript
hyy27952276847 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
逆风优雅7 小时前
vue实现模拟 ai 对话功能
前端·javascript·html
若梦plus7 小时前
http基于websocket协议通信分析
前端·网络协议
不羁。。7 小时前
【web站点安全开发】任务3:网页开发的骨架HTML与美容术CSS
前端·css·html
这是个栗子7 小时前
【问题解决】Vue调试工具Vue Devtools插件安装后不显示
前端·javascript·vue.js