React知识点回顾

268 阅读9分钟

一、类式组件

1、继承

class Person {
    constructor(name,age){
        this.name = name
        this.age = age
    }
}
class Student extends Person{
    constructor(name,age,grade){
        /*
        按理来说是这么写的,继承父类的属性
        this.name = name
        this.age = age
        但比较麻烦,如果父类写一千条,那这里岂不是也要写一千条吗,所以用super
        */
        super(name.age)
        //加上自己的属性
        this.grade = grade
    }
}

子类写构造方法时,必须要加上super,因为你要写新的属性,必须要把父类的属性加上去

2、this指向

class Person {
    constructor(name,age){
        this.name = name
        this.age = age
    }
    study(){
        //study方法放在了哪里? -类的原型对象上,供实例使用
        //通过Person实例调用study时,study中this就是Person实例
    console.log(this);
	}
}

const p1 = new Person('tom',18)
p1.study() //通过实例调用study方法。this指向是Person实例(Person{....})
const x = p1.study //将方法直接赋给x,相当于给地址给了它
x() //相当于直接调用study方法,此时this指向为window,但方法里开启了严格模式,所以为undefind


3、改变this指向

class Weather extends React.Component{
    constructor(props){
        super(props)
        this.state = {isHot: false}
        //这里将this的指向绑定了,详细讲就是将此处的this(调用的实例对象),绑定到了实例对象上面,比如zs调用这个方法,那么zs的this就(前面的this)永远绑定到了zs的这个对象上面(括号里的this)身上,就是将this永远指向自己;换言之就是将this指向的值永远指向括号里面的对象
        this.chageWeather = this.changeWeather.bind(this) 
    }
    render(){
        const {isHot} = this.state
        //因为前面用bind将this的指向变为了实例对象,所以此处将方法赋值给它的同时,也将this指向的地方赋值给了它
        render <h1 onClick = {this.changeWeather}>今天天气很{isHot? '炎热' : '凉爽'} </h1> }
        changeWeather(){
            //所以此处this的指向为实例对象
            console.log(this)
        }
}
    ReactDOM.render(<Weather/>.document.getElementById('test'))

4、ref的使用

class Demo extends React.Component{
    const {input} = this.input
    showData = ()=>{
        alert(input.value)
    }
    render(){
        return(
            //c这个参数为当前input节点。即input本身
            <input ref = {c=>{this.input = c}} type = "text" placeholder = "点击按钮提示数据"/> 
        	<button onClick = {this.showData}>点击</button>
        )
    }
}

ref一般用来联系多个节点,如button点击按扭输出input节点的文本框内容。如果本节点调用本节点的值,那么只就不需要用到ref,ref尽量少用,如:

class Demo extends React.Component{
    showData = ()=>{
        //event.target就是标签自身节点,即标签本身
        alert(event.target.value)
    }
    render(){
        return(
        	<input onBlur = {this.showData} type = "text" placeholder = "失去焦点提示数据">
        )
    }
}

5、受控组件和非受控组件

非受控组件(实现表单提交,提示框提示input中输入的账号和密码)

class Demo extends React.Component{
    handleSubmit = (event) => {
        event.preventDefault() //阻止表单提交
        const { input1,input2 } = this
        //$为转义字符
        alert(`你输入的用户名为: ${input1.value} 你输入的密码为: ${input2.value}`)
    }
    render(){
    return(
    <form onSubmit = { this.handleSubmit }>
     	用户名: <input ref = { c => this.input1 = c } type = "text" name = "username"/>
        密码: <input ref = { c => this.input2 = c } type = "text" name = "password"/>
        <button>登录</button>
     </form>
    )
}
}

受控组件

class Demo extends React.Components{
    state = {
        username: '',
        password: ''
    }
	saveUsername = () => {
         this.setstate({ username: event.target.value })
    }
    savePassword = () => {
        this.setstate({ password: event.target.value })
    }
    handleSubmit = () => {
        alert(this.state.username+this.state.password)
    }
    render(){
        return(
            <form onSubmit = { this.handleSubmit }>
            用户名: <input onChange = { this.saveUsername } type = "text" name = "username"/>
            密码: <input  onChange = { this.savePassword } type = "text" name = "password"/>
            <button>登录</button>
         	</form>
        )
    }
}

非受控组件就是当你点击按钮才能获得输入框的值;受控组件它是实时更新的,它值就一直在那里,直接取就行了

6、高阶函数的柯里化

函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

为了避免上面例子要创建两个函数的尴尬,这里要用到高阶函数的柯里化,只创建一个函数就满足上述需求

class Demo extends React.Components{
    state = {
        username: '',
        password: ''
    }
	saveFormData = (val) => {
        return(
        	(event) => {
                //疑点,这里的val如果不加个[],就相当与"val"
                this.setState([val]: event.target.value)
            }
        )
    }
    handleSubmit = () => {
        alert(this.state.username+this.state.password)
    }
	render(){
        return(
            <form onSubmit = { this.handleSubmit }>
            //这里返回的是一个函数的执行结果,所以上面要用函数的柯里化让它返回的是一个函数,将state的值进行event.target更改
            用户名: <input onChange = { this.saveFormData('username') } type = "text" name = "username"/>
            密码: <input  onChange = { this.saveFormData('password') } type = "text" name = "password"/>
            <button>登录</button>
         	</form>
        )
    }
}

疑点解答

这里的中括号obj[myHero]相当于将2.myHero的值'hero'作为属性名传递给obj。所以第5行的代码就相当于console.log(obj.hero)。其中1.hero和2.hero的必须相等,即为同一个字符串

		let obj = {};
        obj.hero= '亚索';		//1.hero
        let myHero = 'hero'; //myHero变量  2.hero
        console.log(obj.myHero);//undefined,访问不到对应的属性
        console.log(obj[myHero]);//亚索

7、生命周期简介

字体会在1秒内慢慢消失掉

class Life extends React.Component{
    state = { opacity:1 }
	death = () =>{
        //卸载组件
        ReactDOM.unmountComponentAtNode(document.getElementById('test'))
    }
    //组件挂载完毕之后调用这个钩子,只会执行一次
    componentDidMount(){
        this.timer = setInterval(() => {
            let { opacity }  = this.state
            opacity -=0.1
            if(opacity <= 0)opacity = 1
            this.setState(opacity)
        },200)
    }
	//组件将要卸载会调用此钩子
	componentWillUnmount(){
        clearInterval(this.timer)
    }
	//组件内的内容每次更新就会调用render,因此如果将定时器放在render里面,会陷入到死循环当中
	render(){
        console.log('render');
        return(
        	<div>
            <h2 style = {{opacity:this.state.opacity}}React复习中</h2>
			<button onClick = {this.death}>不活了</button>
			</div>
        )
    }
}

新知识

1、a:a 可以写为a,如let b = 2; let a = {b:b}, 可写为let a = {b}

react的diffing算法:

image.png

2、每次重新渲染会和之前的数据作比较,用map遍历时最好不要用当中的index值作为key值,因为如果在原数组当中新增一个数据,下标为0,那么就会重新生成虚拟dom,从而引发效率问题,所以最好用id作为key的唯一标识

3、解构赋值:const a = this.b.a =>

​ const {a} = this.b =>

​ const {b:{a}} = this =>

​ 将a的名字换成c(重命名)

​ const {b:{a:c}} = this

4、输出error信息时要用error.message

8、setState

setState更新状态的2种写法

setState(stateChange, [callback])-----对象式的setState
	stateChange为状态改变对象(该对象可以体现出状态的更改)
	callback是可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用
    
    
    
 setState(updater, [callback])-----函数式的setState
	updater为返回stateChange对象的函数
    updater可以接收到state和props
    callback是可选的回调函数,它在状态更新、界面也更新hou(render调用后)才被调用

总结:1、对象式的seState是函数式的setState的简写方式(语法糖)

​ 2、使用原则:

​ (1).如果新状态不依赖于原状态 ===>使用对象方式

​ (2).如果新状态依赖于原状态===>使用函数方式

​ (3).如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取

class Demo extends Component{
    state = {count: 0}
	add = () =>{
        const {count} = this.state
        this.setState({count:count+1})
        console.log(state)
    }
    reder(){
        return (
        	<div>
            	<h1>当前求和为: {this.state.count}</h1>
				<button Onclick = {this.add}>点我+1</button>
            </div>
        )
    }
}

点击一下按钮后,标签页面展示的是1,但console.log输出的是0,这是因为它是异步进行的

可以改为:

this.setState((state)=>({count: state.count+1}))

这里的参数除了state还可以传props,这里只传了一个参

9、lazyLoad懒加载

import React,{ Component,lazy } from 'react'
import { NavLink,Route } from 'react-router-dom'
/*
	不能用import Home from './Home'
		 import About from './About'
	这种方式引入路由组件,因为这样会直接把路由组件数据加载进去,起不到点一下,加载一下的效果
*/
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))

上述方式引入懒加载后,必须要在注册路由那里加上一些代码,当网比较慢时,可以给一些加载中的数据或者页面提醒客户正在加载中

<Suspense fallback = {<h1>正在加载中....<h1>}>
    <Route path = "/about" component = {About}/>
    <Route path = "/home" component = {Home}/>
</Suspense>

10、路由

路由的基本使用

index.js中要用一个大的路由器BrowserRouter包裹着

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from './App'

ReactDOM.render(
    <BrowserRouter>
    <App/>
    </BrowserRouter>,
    document.getElementById('root'))

About和Home子组件要展示的内容

About.jsx

import React, { Component } from 'react'

export default class index extends Component {
    render() {
        return (
           <h3>我是About的内容</h3>
        )
    }
}

Home.jsx

import React, { Component } from 'react'

export default class index extends Component {
    render() {
        return (
           <h3>我是Home的内容</h3>
        )
    }
}

App.jsx

import React, { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
    render() {
        return (
            <div>
            //相当于一个超链接,点击之后会跳转到to后面的地址
                <Link to = "/about">About</Link><br/>
                <Link to = "/home">Home</Link>
            //跳转之后的地址,其中地址名要与上面的一一对应
                <Route path = "/about" component = {About}></Route>
                <Route path = "/home" component = {Home}></Route>
            </div>
        )
    }
}

高亮效果NavLink

高亮效果就是点击一个按钮,那个按钮持续性的保持那个颜色

将Link替换成NavLink

<div>
     <NavLink to = "/about">About</NavLink><br/>
     <NavLink to = "/home">Home</NavLink>
     <Route path = "/about" component = {About}></Route>
     <Route path = "/home" component = {Home}></Route>
</div>

并且可以给他加属性名,每点击一下就给它加一下属性名

<div>
     <NavLink activeClassName = "yss" to = "/about">About</NavLink><br/>
     <NavLink activeClassName = "yss" to = "/home">Home</NavLink>
     <Route path = "/about" component = {About}></Route>
     <Route path = "/home" component = {Home}></Route>
</div>

随后可以根据这个属性名加一些样式

封装NavLink组件

封装NavLink组件可以将

About

简写成

About

import React, { Component } from 'react'

export default class MyNavLink extends Component {
    render() {
        return (
           <NavLink activeClassName = "atguigu" className = "list-group-item" {...this.props}>
        )
    }
}

引入封装好的MyNavLink组件

<MyNavLink to = "/about" a = {1}, b = {2}>About</MyNavLink>
<MyNavLink to = "/home">Home</MyNavLink>

按照正常情况来讲在子组件中间加一段文字内容,不会进行展示的,但是NavLink标签里面有个children属性,可以用来接收About和Home并将其进行展示,起到<span>你好</span>的作用

Switch的使用

<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
<Route path = "/home" component = {Test}></Route>

此时页面会展示三条数据,也就是路由匹配到了home的路由还会往下继续进行匹配,这样就影响了效率,所以此时为了避免这种情况,需要用到switch

import{Route,Switch} from 'react-router-dom'

<Route path = "/about" component = {About}></Route>
<Route path = "/home" component = {Home}></Route>
<Route path = "/home" component = {Test}></Route>

加上switch后,当匹配到Home之后就不会继续往下找Test了

解决样式丢失问题

<MyNavLink to = "/atguigu/home">Home</MyNavLink>
<MyNavLink to = "/atguigu/home">Home</MyNavLink>
<Switch>
    <Route path = "/atguigu/about" component = { About }/>
    <Route path = "/atguigu/home" component = { Home }/>
</Switch>

当加了加了个地址之后,可能会出现样式丢失问题,这是应为,你加了个/atguigu,导致你请求的bootsrap路径错了

3种解决方法

1用HashRouter,只读取井号前面的路径

import React from 'react'
import React from 'react-dom'
import { HashRouter } from 'react-router-dom'
import App from './App'

ReactDOM.render(
	<HashRouter>
    </App>
    </HashRouter>
)

2.加绝对路径 ‘%PUBLIC_URL%’

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
	//加绝对路径
	<link rel = 'icon' href = '%PUBLIC_URL%/favicon.ico'>
    <link rel = 'style' href = '%PUBLIC_URL%/css/bootstrap'>
</head>
<body>
    <div id="root"></div>
</body>
</html>

3.去除路径css前面的点(.),这样他就会只找css文件下的文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
	//加绝对路径
	<link rel = 'icon' href = '/favicon.ico'>
    <link rel = 'style' href = '/css/bootstrap'>
</head>
<body>
    <div id="root"></div>
</body>
</html>

路由的模糊匹配和严格匹配

模糊匹配

路由默认是模糊匹配,可以比喻为发送地址为商人,接收请求为客户,比如,

//商人
<NavLink to = "/home/a/b">Home</NavLink>
//客户
<Route path = "/home" component = {Home}></Route>

这样写的话可以正常显示,因为客人要的地址商人全都有,但是顺序不能变,比如,

//商人
<NavLink to = "/a/home/b">Home</NavLink>
//客户
<Route path = "/home" component = {Home}></Route>

这样就会报错,且商人只能多给,不能少给,比如,

//商人
<NavLink to = "/home">Home</NavLink>
//客户
<Route path = "/home/a/b" component = {Home}></Route>

也会报错,因为我要的东西你全都没有

严格匹配

如果想要地址匹配必须一模一样,这时可以用exact

//商人
<NavLink to = "/home">Home</NavLink>
//客户
<Route exact path = "/home/a/b" component = {Home}></Route>

Redirect重定向的使用

就是每一个路由都匹配不上的时候,那么就听从Redirect的发落

比如:

import { NavLink, Route, Redirect } from 'react-router-dom'

<NavLink to = "/about">About</NavLink><br/>
    <NavLink to = "/home/a/b">Home</NavLink>
    <Route path = "/about" component = {About}></Route>
    <Route exact path = "/home" component = {Home}></Route>
	<Redirect to = "/about"/>

当页面初始加载时,会进行重定向,起到一个默认选项的作用,会重定向到Redirect指定的地址

二级路由

爷组件App

import React, { Component } from 'react'
import { NavLink, Route, Redirect } from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
    render() {
        return (
            <div>
                <NavLink to = "/about">About</NavLink><br/>
                <NavLink to = "/home">Home</NavLink>
                <Route path = "/about" component = {About}></Route>
                <Route path = "/home" component = {Home}></Route>
                <Redirect to = "/about"/>
            </div>
        )
    }
}

一级路由———父组件About

import React, { Component } from 'react'

export default class About extends Component {
    render() {
        return (
           <h3>我是About的内容</h3>
        )
    }
}

一级路由———父组件Home

import React, { Component } from 'react'
import Message from './Message/index'
import News from './News/index'
import { NavLink, Route } from 'react-router-dom'

export default class About extends Component {
    render() {
        return (
            <>
            <NavLink to = "/home/message">Message</NavLink>
            <NavLink to = "/home/news">News</NavLink>
            <Route path = "/home/message" component = {Message}/>
            <Route path = "/home/news" component = {News}/>
           <h3>我是Home的内容</h3>
           </>
        )
    }
}

二级路由———子组件Message

import React, { Component } from 'react'

export default class Message extends Component {
    render() {
        return (
            <h2>我是Message的内容</h2>
        )
    }
}

二级路由———子组件News

import React, { Component } from 'react'

export default class News extends Component {
    render() {
        return (
            <h2>我是News的内容</h2>
        )
    }
}

在当前章节知识点中唯一需要注意的一点是,父组件Home用路由给子组件传递地址时需要在地址名前加上App的一级路由名:/home/message和/home/news前面的home,因为它在匹配时会拿着这个地址名去一级路由那里去进行匹配地址,匹配完毕后再来二级路由这里,所以本章节知识点当中不可用严格模式匹配。