React笔记

一、组件三大属性

每个组件的this上都有三个重要属性:

  1. state
  2. props
  3. refs

1.state

state称为状态,状态驱动着页面的改变

class Person extends Component {
    
    state = {
        name="张三",
        age:18
    }
	
	/*
	 此处必需写箭头函数
	 不然按钮处就需要写this.change.bind(this),确保this不丢失
	*/
	change=()=>{
        /*
        更新状态是合并,不是覆盖,即传入参数{name:"李四"}
        与原装胎对比,不会覆盖原状态中的age
        */
        this.state({name:"李四"})
    }
	
    render() {
        return (
            <div>
				{<span>{this.state.name}</span>}
                <button onClick={this.change}>点我更新状态</button>
            </div>
        )
    }
}

2.props

父组件传递给子组件的参数

class Father extends Component {
    state = {
        bear:{
            x:1,
            y:2
        }
    }

    render() {
        return (
            <div>
                <Child x={1} y={2}></Child>
            </div>
        )
    }
}

/*
或者:
将bear对象结构,得到x,y,相当于分别传递
<Child {...this.state.bear}></Child>
*/

子组件中可对接收到的参数进行限制,需引入React库中的PropTypes进行限制

//npm i prop-types

import PropTypes from 'prop-types'

class Child extends Component {
    //静态属性
    static propTypes={
        //支持链式写法
        x:PropTypes.string.isrequired
        //类型以小写开头,以区别原生,如:string,object,bool
        //ps:函数类型:func
    }
	
	//默认值
	static defaultProps={
        x:1
    }

    render() {
        return (
            <div>
				{this.props.x}
            </div>
        )
    }
}

/*
或者:
Child.propTypes={
	x:PropTypes.func
}
*/

3.refs

使用ref可直接拿到组件的原生DOM对象

//有三种写法获得ref

//1.字符串式(不推荐)
class Person extends Component {
	
    render() {
        console.log(this.refs)
        return (
            <div>
				<span ref="demo"></span>
            </div>
        )
    }
}

//2.回调函数式
class Person extends Component {
	/*
	  函数式会将ref作为参数传递给回调函数,即c,
      可直接写在类实例属性上,即this.test=c,
      这样可不用去refs里取
    */
    render() {
        return (
            <div>
				<span ref={c=>{this.test=c}}></span>
            </div>
        )
    }
}

二、生命周期

旧生命周期

image-20210723201728475

初始化

  1. constructor(构造函数)
  2. componentWillMount(将要挂载)
  3. render(渲染函数)
  4. componentDidMount(组件已经挂载)

更新时

  1. shouldComponentUpdate(控制是否更新)
  2. componentWillUpdate(组件将要更新)
  3. render(渲染函数)
  4. componentDidUpdate(组件已经更新)

卸载时

可由ReactDOM.unmountComponentAtNode()手动触发组件卸载

  1. componentWillUnmount(组件将要卸载)

注意

  • shouldComponentUpdate控制是否允许此次更新,返回true则允许更新,继续后面流程,返回false则中断后面流程,不返回值则报错。
  • 组件上的forceUpdate()方法,可强制更新,跳过shouldComponentUpdate控制。

新生命周期

image-20210723202250778

初始化

在constructor和render之间加入getDerivedStateFromProps钩子,用法比较罕见,一般在state全受prop控制时使用,以下摘自官网:

image-20210723202747080

更新时

同初始化时,在shouldComponentUpdate前加入钩子:getDerivedStateFromProps,不过还在componentDidUpdate前加入,getSnapshotBeforeUpdate(更新前获得组件快照),用法也比较罕见,以下摘自官网:

image-20210723203020329

卸载时

同旧的生命周期钩子。

即将废弃的钩子

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

使用会出现警告,最好不使用,或者前面带上UNSAFE_前缀来使用。

总结

新的生命周期相交于旧的

废弃了三个不重要的钩子:将要挂载,将要收到参数,将要更新

更新了两个罕见的钩子:获得props驱动的state,更新前获得组件快照

总的来说,重要的钩子有三个:

  1. render,渲染函数
  2. componentDidMount,组件挂载完成,一般做发起请求,初始化等工作。
  3. componentWillUnmount,组件即将卸载,一般做收尾工作。

三、React原理简述

React组成

  1. react.js,React核心库。
  2. react-dom.js,提供操作DOM的react核心库,依赖于上述库。
  3. babel.js,用于解析jsx,将其转换为js代码的库。

Vitual Dom和Diff算法

相较于直接操作较大的真实(与虚拟相对)DOM对象,操作JS里的对象可优化浏览器性能,减少重排和重绘的次数。

故将真实的浏览器DOM对象映射成JS里的对象(虚拟DOM),每次数据更新,驱动视图的改变时,查看虚拟DOM有没有受影响,未受影响,则复用,否则重新渲染。

//每个dom都有一个key值,用以区分每个虚拟DOM

class Person extends Component {
    state = {
        arr=[
        	{id:1,name:'老张',age:30},
        	{id:2,name:'老王',age:20}
        ]
    }

    render() {
        //此处key值不推荐(不是不可以)使用数组下标index
        const {arr}=this.state
        return (
            <ul>
				arr.map((item,index)=>{
                    return <li key={index}>{item.name}-{item.age}</li>
                })
            </ul>
        )
    }

	/*
		 render() {
        //此处key值不推荐使用数组下标index
        若dom中有一些"未改变"(要复用的DOM)(若正好是输入类,则很危险)
        则一些破坏原数组,如:逆序插入
        的方法可能导致每个li和input错位
        const {arr}=this.state
        return (
            <ul>
				arr.map((item,index)=>{
                    return <li key={index}> {item.name}-{item.age} <input/></li>
                })
            </ul>
        )
    }

	*/
}

四、Router路由

1.实现原理

SPA(Single Page APP)

  1. 只有一个完整页面
  2. 点击页面中的链接,不会刷新页面,只会做局部更新

原生实现

通过监听Window上的Hash或History的变化,来实现页面的更新,如下:

		//方法一,直接使用H5推出的history身上的API
		// let history = History.createBrowserHistory() 
		//方法二,hash值(锚点)
		let history = History.createHashHistory() 
		
        //历史记录压栈
		function push (path) {
			history.push(path)
			return false
		}
		
   		//替换掉栈顶的历史记录
		function replace (path) {
			history.replace(path)
		}
		
		//历史记录后退
		function back() {
			history.goBack()
		}
		
		//历史记录前进
		function forword() {
			history.goForward()
		}

		history.listen((location) => {
			console.log('请求路由路径变化了', location)
		})

前进和后退对应浏览器URL左边的:image-20210723205406125

2.使用

react-router-dom提供组件:

  • BrowserRouter、HashRouter,一般包在APP组件外边,决定是哪种路由方式。
  • Route 注册路由
  • Redirect 重定向
  • Link、NavLink、Switch 决定路由跳转

例一:Route和Switch的使用

入口文件:

//引入react核心库
import React from 'react'
//引入ReactDOM
import ReactDOM from 'react-dom'
//引入BrowserRouer路由组件
import { BrowserRouter } from 'react-router-dom'
//引入App
import App from './App'

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

App:

import React, { Component } from 'react'
import {NavLink,Route} from 'react-router-dom'
import Home from './pages/Home' //Home是路由组件
import About from './pages/About' //About是路由组件
import Header from './components/Header' //Header是一般组件

export default class App extends Component {
	render() {
		return (
			<div>
				<div>		
					<Header/>		
				</div>
				<div>
					<div>
						{/*类似于原生中的a标签 */}
						<NavLink to="/about">About</NavLink>
						<NavLink to="/home">Home</NavLink>
					</div>
				<div>
				<div>
					{/* 注册路由 */}
					<Route path="/about" component={About}/>
					<Route path="/home" component={Home}/>
				</div>
			</div>
		)
	}
}

NavLink和Link差别:NavLink可给activeClassName,activeStyle等属性

Switch作用:只渲染一个匹配到的第一个路由,不会渲染多个,如下:

//若无Switch包裹,当路径为/home时以下三个组件皆渲染
<Route path="/home" component={Home}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>

//包裹起来确保只渲染匹配到的第一个
<Switch>
	<Route path="/home" component={Home}/>
	<Route path="/:user" component={User}/>
	<Route component={NoMatch}/>
</Switch>

例二:精准匹配和模糊匹配

//路由导航
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home/a/b">Home</MyNavLink>

//注册路由
<Switch>
    <Route exact path="/"      component={Root} />
	<Route exact path="/about" component={About}/>
	<Route exact path="/home"  component={Home}/>
</Switch>

**exact表示开启路由精准匹配,**如:路径/home/a/b必需精准对应某个路由的path,该路由才能进行渲染。

Route默认是模糊匹配,如:路由Root的path是/home/a/b的字串,匹配上了,优先进行渲染;若未使用Switch组件,则Root和Home一并渲染。

工程中一般都是模糊匹配

例三:重定向

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

重定向组件一般对路由进行兜底,如上,若路径/home和/about都未匹配上,则使用Redirect组件,发现其路径是/about,故匹配About组件。

例四:嵌套路由

App:

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

Home:

<MyNavLink to="/home/news">News</MyNavLink>
<MyNavLink to="/home/message">Message</MyNavLink>
							
<Switch>
	<Route path="/home/news"    component={News}/>
	<Route path="/home/message" component={Message}/>
	<Redirect to="/home/news"/>
</Switch>

3.路由传参

路由组件的props

//每个路由组件上都有history、location、match三个对象

//上面有go、goBack、goForword、push、replace等方法
history:{
    //或POP
    action:"PUSH",  
    //路由栈的长度,
    length:4    
    location:{....}
}

//来自hisroty上的location
location:{
    hash: ""
	key: "t3t15u"
	pathname: "/home/message/detail/"
    //查询字符串
	search: "?id=5&title=消息1"
    //通过state传递的参数
	state: undefined
}

//一些匹配数据,主要是path和params
match:{
    //是否是精准匹配
    isExact: true
    //params传递的参数
	params: {}
	path: "/home/message/detail"
	url: "/home/message/detail/"
}

例一:传递params参数

<Link to={`/home/message/detail/5/${title}`}>新闻标题</Link>

//声明接受params参数
<Route path="/home/message/detail/:id/:title" component={Detail}/>

路由组件:

// 接收params参数
const {id,title} = this.props.match.params

例二:传递state参数

实际上,route的to写成字符串是一种简写形式,完整形式应该为一个对象,对象有一个key为state

const routerObj={pathname:'/home/message/detail',state:{id:5,title:'参数'}
<Route to={routerObj}/>

路由组件:

// 接收state参数
const {id,title} = this.props.location.state || {}

例三:传递search参数

//查询字符串
<Link to={`/home/message/detail/?id=5&title=${title}`}>新闻标题</Link>

//正常注册路由
<Route path="/home/message/detail" component={Detail}/>

路由组件:

// 接收search参数
const {search} = this.props.location
const {id,title} = qs.parse(search.slice(1))

路由传参总结

除了params传递参数需要在Route声明接受外,其它的都正常注册,然后组件内部在props上取得参数。

Search查询字符串方法较麻烦,一般不用。

4.高级使用

例一:replace路由

<Link replace to="/home">Home</Link>

replace表示匹配的路由不是压栈,而是代替栈顶的路由。

例二:编程式路由导航

路由组件:

	replaceShow = (id,title)=>{
		//replace跳转+携带params参数
		//this.props.history.replace(`/home/message/detail/${id}/${title}`)

		//replace跳转+携带search参数
		// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)

		//replace跳转+携带state参数
		this.props.history.replace(`/home/message/detail`,{id,title})
	}

	pushShow = (id,title)=>{
		//push跳转+携带state参数
		this.props.history.push(`/home/message/detail`,{id,title})
		
	}

	back = ()=>{
        //栈顶指针向下移动1位
		this.props.history.goBack()
	}

	forward = ()=>{
        //栈顶指针向上移动1位
		this.props.history.goForward()
	}

	go = ()=>{
		this.props.history.go(-2)
	}
    
    <button onClick={()=>this.pushShow(5,'test')}>跳转</button>

例三:withRouter的使用

一般组件是没有路由组件上面的hisroty等属性和方法的,所以引入了withRouter。

import { withRouter } from "react-router-dom";

class Header extends Component {
	render() {
        //可以看到history、location、match属性
        console.log(this.props)
		return (
			<div>
				Test
			</div>
		)
	}
}

//包裹一层
export default withRouter(Header)

五、组件通信

1.pubsub-js 消息订阅

pubsub-js是一个消息订阅库,可在任意框架中使用。

//下载
npm install pubsub-js --save
//组件中引入
import PubSub from 'pubsub-js' 

//订阅方:
//订阅消息,回调的第一个参数为消息名(delete)(饱受诟病)
this.id= PubSub.subscribe('delete', (title,data)=>{})
//组件销毁前要取消订阅
PubSub.unsubscribe(this.id)

//发布方:
//发布消息
PubSub.publish('delete', '我是参数')

2.redux

redux是一个专门用于做状态管理的JS库(不是react插件库),可在任意框架中使用,但一般只在react中使用。

工作流程:

image-20210723221302483

文件结构:

image-20210723221546973

constant中定义枚举类型

/* 
	该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'

actions中一般导出一个action的创建函数

/* 
	该文件专门为Count组件生成action对象
*/
import {INCREMENT,DECREMENT} from '../constant'

//同步action,就是指action的值为Object类型的一般对象
export const increment = data => ({type:INCREMENT,data})
export const decrement = data => ({type:DECREMENT,data})

//异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const incrementAsync = (data,time) => {
	return (dispatch)=>{
		setTimeout(()=>{
			dispatch(increment(data))
		},time)
	}
}

reducers中创建出reducer

/* 
	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
import { INCREMENT, DECREMENT } from '../constant'

const initState = 0 //初始化状态
//导出reducer
export default function countReducer(preState = initState, action) {
	//从action对象中获取:type、data
	const { type, data } = action
    
	//根据type决定如何加工数据
	switch (type) {
		case INCREMENT: //如果是加
			return preState + data
		case DECREMENT: //若果是减
			return preState - data
		default:
			return preState
	}
}

srote一般汇总所有reducer

/* 
	该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore,applyMiddleware} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'

//暴露store
export default createStore(countReducer,applyMiddleware(thunk))

使用

如在count组件中使用:

//引入store
import Store from '../../redux/store'

//获得数据
Store.getState()

//执行action
/*
dispatch接受一个参数
1.形如{type:'',data:''}的对象*
*/
Store.dispatch(createIncrementAction(value*1))
/*
2.一个函数,若检测到是一个函数,则将dispatch传入
*/
store.dispatch(createIncrementAsyncAction(value*1,500))

3.react-redux

facebook根据redux特性推出的官方插件库

工作流程

image-20210723223615253

使用:

在App的外层套上Provider组件,并传递给Provider组件创建出来的store

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './redux/store'
import {Provider} from 'react-redux'

ReactDOM.render(
	/* 此处需要用Provider包裹App,目的是让App所有的后代容器组件都能接收到store */
	<Provider store={store}>
		<App/>
	</Provider>,
	document.getElementById('root')
)

将普通组件变为容器组件

//引入connect用于连接UI组件与redux
import {connect} from 'react-redux'

//定义UI组件
class Count extends Component {
    render(){
        console.log(this.props)
        return (
        	<div></div>
        )
    }
}


/* 
1.返回一个对象
2.key传递给props的key,value传递给props的value
3.参数state是store中的状态
*/
function mapStateToProps(state){
	return {count:state}
}

/* 
1.返回一个对象;
2.key传递给props的key,value传递给props的value
3.参数dispatch是store的行为
*/
function mapDispatchToProps(dispatch){
	return {
		jia:number => dispatch(createIncrementAction(number)),
		jian:number => dispatch(createDecrementAction(number)),
		jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
	}
}


/*
connect第一个函数接受两个参数:
1.mapStateToProps
2.mapDispatchToProps
将redux中的数据和操作映射到props上
connect第二个函数接受UI组件
*/
export default connect(mapStateToProps,mapDispatchToProps)(Conut)

多组件多状态

Reducers的入口文件

import {combineReducers} from 'redux'
import count from './count'
import persons from './person'

//在reducer文件夹的入口文件汇总所有的reducer变为一个总的reducer
export default  combineReducers({
    //此时store的state对象上有count和persons
	count,persons
})

Store文件

import {createStore,applyMiddleware} from 'redux'
import reducer from './reducers'		//汇总后的reducer
//引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'			
 //引入redux-devtools-extension,便于开发者工具观察
import {composeWithDevTools} from 'redux-devtools-extension' 

//暴露store 
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))

Person的action

import {ADD_PERSON} from '../constant'

//创建增加一个人的action动作对象
export const addPerson = personObj => ({type:ADD_PERSON,data:personObj})

Person的reducer

import {ADD_PERSON} from '../constant'

//初始化人的列表
const initState = [{id:'001',name:'tom',age:18}]

export default function personReducer(preState=initState,action){
	// console.log('personReducer@#@#@#');
	const {type,data} = action
	switch (type) {
		case ADD_PERSON:
            //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。
			//preState.unshift(data) 
            //返回新的state
			return [data,...preState]
		default:
			return preState
	}
}

Person组件

//引入actions的creator
import {addPerson} from '../../redux/actions/person'

export default connect(
    //映射状态:从state上结构出persons和count并传递给props(简写)
	({persons,count}) => ({persons,count}),
	//映射操作:拿到dispatch
    (dispatch)=>({addPerson:personObj => dispatch(addPerson(personObj))}) //可将函数简写成对象:{addPerson}
)(Person)

4.补充

  • 每个Reducer必须是纯函数,即对于任意一个参数,任何时间、条件下,都返回唯一确定的值,如:func(1)=2,每次调用func并传入参数1都返回2。纯函数内部一般不调用随机数、时间参数等不确定的值,且不改变参数、不发起网络请求、不调用输入设备

  • Store初始化时会调用一次Reducer,传入的preState为undefined,此时可赋予state一个初值。

  • 还有诸如react-redux的数据管理插件,如DVA,Mobx…

六、扩展

1.setState的写法

完整写法

state={
    a:1,
    b:2
}

function updater(state,props){
    //返回新的状态
    return {
        a:3
    }
}

setState(updater,[callback])

简写

//因为updater返回一个对象,所以可直接把对象写成第一个参数
setState({a:3},[callback])
  1. callback是可选的回调函数,它在render之后调用
  2. 对象式简写是函数式写法的语法糖
  3. 使用原则:
    1. 如果新状态不依赖于原状态 => 使用对象方式
    2. 如果新状态依赖于原状态 ==> 使用函数方式
    3. 如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取

2.lazy组件实现懒加载

import {lazy,Suspense} from 'react'

//原写法:const Login=import('@/pages/Login')
//懒加载:接受一个回调函数
const Login =lazy(()=>import('@/pages/Login'))

	//通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
	<Suspense fallback={<h1>loading.....</h1>}>
        <Switch>
            <Route path="/xxx" component={Xxxx}/>
            <Redirect to="/login"/>
        </Switch>
    </Suspense>

3.Hooks

1. React Hook/Hooks是什么?

  1. Hook是React 16.8.0版本增加的新特性/新语法
  2. 可以让你在函数组件中使用 state 以及其他的 React 特性

2. 三个常用的Hook

  1. 状态:State Hook: React.useState()
  2. 生命周期:Effect Hook: React.useEffect()
  3. 引用:Ref Hook: React.useRef()

3. State Hook

  1. State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
    语法: const [xxx, setXxx] = React.useState(initValue)
  2. useState()说明:
    参数: 第一次初始化指定的值在内部作缓存
    返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
  3. setXxx()2种写法:
    setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
    setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

4. Effect Hook

  1. Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

  2. React中的副作用操作:

    • 发ajax请求数据获取
    • 设置订阅 / 启动定时器
    • 手动更改真实DOM
  3. 语法和说明:

    useEffect(() => {
    // 在此可以执行任何带副作用操作
    return () => { // 在组件卸载前执行
    }
    }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

  4. 可以把 useEffect Hook 看做如下三个函数的组合
    componentDidMount()
    componentDidUpdate()
    componentWillUnmount()

5. Ref Hook

  1. Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
  2. 语法: const refContainer = useRef()
  3. 作用:保存标签对象,功能与React.createRef()一样

4.Fragment 标签片段

原来的组件必需有个根标签,可使用Fragment来代替根标签,解析时不会解析Fragment。

import {Fragment} from 'react'

<Fragment></Fragment>
//也可使用空标签,但空标签不可拥有key等属性
<></>

5.Context 上下文

理解

一种组件间通信方式, 常用于**【祖组件】【后代组件】**间通信

使用

import {createContext} from 'react'

//1.创建上下文,上面有Provider和Consumer两个组件
const myContext=createContext()

//2.渲染子组件时在外面包裹一层Provider,通过value属性给后代传递数据
<myContext.Provider value={x:1}>
	<Son/>
</myContext.Provider>

//3.后代组件读取
//(1)类中声明读取
static contextType=myContext
const {x}= this.context

//(2)函数与类中均可
<Consumer>{value=>value.x}</Consumer>

注意

在应用开发中一般不用context,一般都用它封装react插件

6.PureComponent 组件优化

问题

以下两个问题导致了组件渲染时效率低下

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render()。
  2. 只要当前组件重新render(), 子组件也会重新render,纵使子组件没有用到父组件的任何数据。

解决思路

只有在state或者props改变时才重新render,可在shouldComponentUpdate中控制,检查是否有变化来更新,但数据多了之后会造成代码冗余难以维护,故引入PureComponent,本质上也是控制了shouldComponentUpdate

使用

import React,{PureComponent} from 'react'

//仅改变了继承的父类
export default class People extends PureComponent{
    render(){
        return (
        	<div>People组件</div>
        )
    }
}

7.render props 插槽

如何向组件内部动态传递带内容的结构

Vue:
<Slot></Slot>

React:
1.使用children props: 直接在组件标签体中写想传递的结构
2.使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

使用

children props

<A>
  <B>xxxx</B>
</A>
A组件中通过this.props.children可拿到 <B>xxxx</B>
问题: B组件拿不到A组件的数据

render props

<A render={(data) => <B data={data}></B>} >  </A>
A组件:通过this.props.render(数据)传递数据
B组件:通过this.props.data读取A组件传入的数据

8.错误边界

理解:

错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

特点:

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

使用方式:

getDerivedStateFromError配合componentDidCatch

export default class Parent extends Component {

	state = {
		hasError:'' //用于标识子组件是否产生错误
	}

	//生命周期函数,一旦后代组件报错,就会触发
	static getDerivedStateFromError(error){
		console.log('@@@',error);
		return {hasError:error}
	}

	componentDidCatch(){
		console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
	}

	render() {
		return (
			<div>
				<h2>我是Parent组件</h2>
				{this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child/>}
			</div>
		)
	}
}

9.开发模式配置代理服务器

方法一

在package.json中追加如下配置

"proxy":"https://后端api"

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 假设前端devServer在3000端口,开启代理后直接请求3000端口即可,不会引起跨域。
  4. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给后端api(优先匹配前端资源

方法二

  1. 第一步:创建代理配置文件

    在src下创建配置文件:src/setupProxy.js
    
  2. 编写setupProxy.js配置具体代理规则:

    const proxy = require('http-proxy-middleware')
    
    module.exports = function(app) {
      app.use(
        proxy('/api1', {  				//api1是需要转发的请求(所有带有/api1前缀的请求都会转发给后端api)
          target: 'http://后端api', 	   
          changeOrigin: true, //控制服务器接收到的请求头中host字段的值
          /*
          	changeOrigin设置为true时,服务器收到的请求头中的host为:后端的地址
          	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
          	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
          */
          pathRewrite: {'^/api1': ''} 	//去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
        }),
        proxy('/api2', { 
          target: 'http://localhost:5001',
          changeOrigin: true,
          pathRewrite: {'^/api2': ''}
        })
      )
    }
    

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值