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代表接受父组件传递过来模板

相关推荐
神仙别闹12 分钟前
基于tensorflow和flask的本地图片库web图片搜索引擎
前端·flask·tensorflow
GIS程序媛—椰子1 小时前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_0011 小时前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端1 小时前
Content Security Policy (CSP)
前端·javascript·面试
木舟10091 小时前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤43911 小时前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢2 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安2 小时前
前端第二次作业
前端·css·css3
啦啦右一2 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习