文章目录
setState的两种写法
setState适用于设置state中数据的方法,有两种编写方式
对象式
对象式的setState的第一个参数是一个对象, 第二个参数是回调函数是可选参数。
语法: setState({修改的属性: 修改后的属性值},()=>{回调函数,等状态修改完之后执行的函数})
setState触发的动作是异步的,所以执行完setState就获取刚才修改的数据可能获取不到最新的数据,而第二个参数回调函数是在render()调用完
之后才进行调用(状态修改完之后再调用) ,所以在回调函数中获取的值是最新修改的值。
import React, { Component } from 'react'
export default class Demo extends Component {
state = {count:0}
add = ()=>{
const {count} = this.state
// setState的两种形式
// 对象式的setState
// 1.只传一个参数传递对象 (参数就是改变后的state数据)
this.setState({count:count+1})
/* 2.传递两个参数
第一个参数仍然是一个对象
第二个参数是一个回调函数,render()调用完之后才进行调用(状态修改完之后再调用)
*/
// setState触发的后续动作是异步的(---react的数据更改是异步的)
this.setState({count:count+1},()=>{
console.log(this.state.count);
})
}
render() {
return (
<div>
<h1>当前求和为:{this.state.count}</h1>
<button onClick={this.add}>点我加1</button>
</div>
)
}
}
函数式
函数式的setState的第一个参数是一个函数,函数可以接收到state和props两个参数,但是函数的返回值是要修改后的对象
语法: setState((state,props)=>{},()=>{回调函数,等状态修改完之后执行的函数})
import React, { Component } from 'react'
export default class Demo extends Component {
state = {count:0}
add = ()=>{
const {count} = this.state
// setState的两种形式
// 函数式setState
// 1.只传一个参数,参数是一个函数,但是函数的返回值是要修改后的对象 (参数就是改变后的state数据)
// 该函数有两个参数state和props
this.setState((state,props)=>{
return {count:state.count}
})
// 2. 传递两个参数的时候,第二个参数仍然是回调函数
}
render() {
return (
<div>
<h1>当前求和为:{this.state.count}</h1>
<button onClick={this.add}>点我加1</button>
</div>
)
}
}
关系
对象式的setState是函数式的setstate的简写方式(语法糖)
使用原则:
- 如果新状态不依赖于原状态,使用对象方式
- 如果新状态依赖于原状态,使用函数方式
layLoad懒加载
懒加载就是用的时候进行加载,如果不用就不进行加载。
懒加载最常用的场景是路由导航。
如果页面导航栏有多个导航,每个导航对应一个组件页面;正常情况下只要加载该页面就会将该页面所有导航对应的导航组件都进行加载。这样不太好,我们想要的效果是点击哪个导航就对应加载哪个组件,这时就可以用到懒加载。
使用方法
- 使用
lazy
引入组件
import { lazy } from 'react'
const Home = lazy(()=>import('./Home'))
const About = lazy(()=>import('./About'))
- 使用
Suspense
包裹注册路由,fallback
参数用于定义组件加载不出来的时候显示的内容
<Suspense fallback={<h1>Loading......</h1>}>
<Routes>
<Route element={<Home />} path="/home"></Route>
<Route element={<About />} path="/about"></Route>
</Routes>
</Suspense>
eg:
import React, { Component, lazy, Suspense } from 'react'
import { NavLink, Route, Routes } from 'react-router-dom'
// import About from './About/index.jsx'
// import Home from './Home'
const Home = lazy(()=>import('./Home'))
const About = lazy(()=>import('./About'))
export default class Demo extends Component{
render() {
return (
<div>
<div className="row">
<div className="col-xs-8 col-xs-offset-2">
<div className = "page-header"><h2>React Router Demo</h2></div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<NavLink className="list-group-item" to="/about">About</NavLink>
<NavLink className="list-group-item" to="/home">Home</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
{/* v6版本的router需要添加父组件<Routes>,注册写法也和v5不同 */}
{/* fallback={<h1>Loading......</h1>} 对应的是懒加载没加载出来的时候显示的内容 */}
<Suspense fallback={<h1>Loading......</h1>}>
<Routes>
<Route element={<Home />} path="/home"></Route>
<Route element={<About />} path="/about"></Route>
</Routes>
</Suspense>
</div>
</div>
</div>
</div>
</div>
)
}
}
点击那个路由导航就显示哪个文件。
注意:
如果fallBack
的属性值是一个组件,那么该组件一定不是使用懒加载进行加载,使用组件正常的引入方式进行引入。
Hooks
hooks是react16版本引入的。
函数式组件中没有this,所有很多功能受限。
hooks可以让我们在函数式组件中使用state以及其他的React特性
。
hooks——useState
- 获取state :
React.useState(initVlaue)
useState(initVlaue) 的参数是该状态的初始值
useState(initVlaue) 的返回值有两个,第一个是存储的状态的值,第二个是修改该状态的方法
eg:函数式组件结合React.useState(a)
实现数子修改
import React, { Component } from 'react'
// 类式组件
// class Demo extends Component {
// state = {
// count:0
// }
// add = () => {
// this.setState((state)=>({count:state.count+1}))
// }
// render() {
// return (
// <div>
// <h2>当前求和为:{this.state.count}</h2>
// <button onClick = {this.add}>点我加1</button>
// </div>
// )
// }
// }
// 函数式组件
function Demo(){
// [a,b]:数组的解构赋值
// useState(0)的参数是该状态的初始值
// useState(0)的返回值有两个,第一个是存储的状态的值,第二个是修改该状态的方法
/**
* 注意:执行add函数,页面数据修改就会重新渲染,重新执行Demo函数,就会重新执行useState(0),这样说的话count就会一直是0,不能进行修改
* 但实际上是可以正常修改的,这是因为react底层做了一个处理
* 当时第一次执行useState的时候将count数据缓存下来,当接下来再执行useState的时候,会使用缓存覆盖count的值
*/
const [count,setCount] = React.useState(0)
const [name,setName] = React.useState('yang')
// 默认没有this
function add(){
// 参数是修改后的状态值
setCount(count+1)
// 函数写法
// setCount(count=>count+1)
}
function changeName(){
setName(name+'1')
}
return (
<div>
<h2>当前求和为:{count}</h2>
<button onClick = {add}>点我加1</button>
<h2>名字是:{name}</h2>
<button onClick = {changeName}>改名</button>
</div>
)
}
export default Demo
hooks——useEffect
- 获取生命周期函数:
React.useEffect(()=>{},[])
第一个参数的回调函数在组件挂载和检测的值修改的时候进行调用,检测的值需要写在第二个参数的数组中:如果检测所有数据改变直接不传第二个参数即可,如果谁也不检测第二个参数需要传空数组。
所以
-
React.useEffect(()=>{},[])
相当于类式组件的componentDidMount
这个钩子 -
React.useEffect(()=>{},[变量名])
相当于类式组件的componentDidUpdate
这个钩子 -
React.useEffect(()=>{return()=>{相当于组件卸载前的钩子}},[])
,React.useEffect第一个参数的返回值相当于类式组件的componentWillUnmount
这个钩子
eg:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
// 类式组件
// class Demo extends Component {
// state = {
// count:0
// }
// add = () => {
// this.setState((state)=>({count:state.count+1}))
// }
// unmount = () =>{
// ReactDOM.unmountComponentAtNode(document.getElementById('root'))
// }
// // 组件一挂载就执行
// componentDidMount(){
// this.timer = setInterval(()=>{
// this.setState((state)=>({count:state.count+1}))
// },1000)
// }
// // 组件卸载之后虚幻定时器不会自动清除,需要我们手动清除
// componentWillUnmount(){
// clearInterval(this.timer)
// }
// render() {
// return (
// <div>
// <h2>当前求和为:{this.state.count}</h2>
// <button onClick = {this.add}>点我加1</button>
// <button onClick = {this.unmount}>卸载组件</button>
// </div>
// )
// }
// }
// 函数式组件
function Demo(){
const [count,setCount] = React.useState(0)
// 默认没有this
function add(){
setCount(count+1)
}
function unmount(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
// 使用生命周期钩子
// 该回调函数在组件挂载和检测的值修改的时候进行调用,检测的值需要写在第二个参数的数组中,如果检测所有数据改变直接不传第二个参数即可,如果谁也监测就传递空数组
// 此时相当于componentDidMount
React.useEffect(()=>{
let timer = setInterval(()=>{
setCount(count=>count+1)
},1000)
// 返回值相当于componentWillUnmount
return ()=>{
clearInterval(timer)
}
},[])
return (
<div>
<h2>当前求和为:{count}</h2>
<button onClick = {add}>点我加1</button>
<button onClick = {unmount}>卸载组件</button>
</div>
)
}
export default Demo
hooks——useRef
在函数式组件中可以使用React.useRef()
来创建ref对象,作用和React.createRef()
的作用一样,用于保存标签对象。
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
// 类式组件
// class Demo extends Component {
// state = {
// count:0
// }
// myRef = React.createRef()
// add = () => {
// this.setState((state)=>({count:state.count+1}))
// }
// unmount = () =>{
// ReactDOM.unmountComponentAtNode(document.getElementById('root'))
// }
// show = () => {
// alert("当前值:", this.myRef.current.value)
// }
// // 组件一挂载就执行
// componentDidMount(){
// this.timer = setInterval(()=>{
// this.setState((state)=>({count:state.count+1}))
// },1000)
// }
// // 组件卸载之后虚幻定时器不会自动清除,需要我们手动清除
// componentWillUnmount(){
// clearInterval(this.timer)
// }
// render() {
// return (
// <div>
// <input type= "text" ref={this.myRef}/>
// <h2>当前求和为:{this.state.count}</h2>
// <button onClick = {this.add}>点我加1</button>
// <button onClick = {this.unmount}>卸载组件</button>
// <button onClick = {this.show}>展示当前输入框的值</button>
// </div>
// )
// }
// }
// 函数式组件
function Demo(){
const [count,setCount] = React.useState(0)
const myRef = React.useRef()
// 默认没有this
function add(){
setCount(count+1)
}
function unmount(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
function show(){
alert(myRef.current.value)
}
// 使用生命周期钩子
// 该回调函数在组件挂载和检测的值修改的时候进行调用,检测的值需要写在第二个参数的数组中,如果检测所有数据改变直接不传第二个参数即可,如果谁也监测就传递空数组
// 此时相当于componentDidMount
React.useEffect(()=>{
let timer = setInterval(()=>{
setCount(count=>count+1)
},1000)
// 返回值相当于componentWillUnmount
return ()=>{
clearInterval(timer)
}
},[])
return (
<div>
<input type="text" ref={myRef}></input>
<h2>当前求和为:{count}</h2>
<button onClick = {add}>点我加1</button>
<button onClick = {unmount}>卸载组件</button>
<button onClick = {show}>点我提示数据</button>
</div>
)
}
export default Demo
fragment
<Fragment>
标签在react解析的时候不会被保存,可以较少层级结构。 <></>
标签在react解析的时候也不会被解析,但是和fragment的区别是fragment标签可以有key属性。
import React, { Component,Fragment } from 'react'
export default class index extends Component {
render() {
return (
// fragment在react解析的时候不会被保存,可以较少层级结构
// <></>标签在react解析的时候也不会被解析,但是和fragment的区别是fragment标签可以有key属性
<Fragment>
<div>index</div>
<div>index</div>
</Fragment>
)
}
}
Context
一种组件间通信的方式。context常用于父组件和所有后代组件
之间的通信。
步骤:
- 引入context
const MyContext = React.createContext()
- 祖先组件传递数据, value = ‘值’ 的形式传递, 存在
this.context
中
<MyContext.Provider value={{username,age}}>
<B/>
</MyContext.Provider>
- 子组件声明接收,子组件只有生命接受才能获取到this.context的值
类式组件声明接收:
static contextType = MyContext
函数或类式组件都可以用的声明接收
const MyContext = React.createContext()
<MyContext.Consumer>
{
value =>{
return `姓名是${value.username},年龄是${value.age}`
}
}
</MyContext.Consumer>
eg:
import React, { Component } from 'react'
const MyContext = React.createContext()
export default class A extends Component {
state = {username:'yang', age:18}
render() {
const {username,age} = this.state
return (
<div style = {{width:'500px',background:'orange',padding:'8px'}}>
<div>A组件</div>
<h4>用户名:{username }</h4>
{/* 子组件通过value将数据进行传递 */}
<MyContext.Provider value={{username,age}}>
<B/>
</MyContext.Provider>
</div>
)
}
}
class B extends Component {
render() {
return (
<div style={{width:'100%',background:'pink',padding:'8px'}}>
<div>B组件</div>
<h4>从A接受的用户名:{this.props.username}</h4>
<C/>
</div>
)
}
}
// class C extends Component {
// // 声明接收祖先组件context中的内容(只有声明了才接受的到)
// static contextType = MyContext
// render() {
// const { username,age} = this.context
// return (
// <div style={{width:'100%',background:'yellow',padding:'8px'}}>
// <div>C组件</div>
// <h4>从A接受的用户名:{username}, 年龄是:{age}</h4>
// </div>
// )
// }
// }
function C() {
return (
<div style={{width:'100%',background:'yellow',padding:'8px'}}>
<div>C组件</div>
<h4>
<MyContext.Consumer>
{
value =>{
return `姓名是${value.username},年龄是${value.age}`
}
}
</MyContext.Consumer>
</h4>
</div>
)
}
组件优化
pureComponent
问题:
- 问题一:当父组件中使用子组件时,只要父组件的数据发生改变,不管子组件中是否引入了父组件的数据,子组件都会被重新渲染(重新执行render函数),我们希望如果子组件不使用父组件的数据,当父组件重新渲染的时候不会使子组件也重新渲染以节省资源。
- 问题二:当我们使用
this.setState({})
时其实谁也没改,但是仍然会去更新一状态,但是谁也改不掉,之后再去调用render()
函数使更新渲染页面。即只要使用了setState
就会调用render重新渲染页面。我们希望的是他能识别,当传入的数据不能修改状态时就不进行状态更新不调用render。
优化目标:
只有当组件的state
或props
数据发生改变的时候才重新render()。
产生问题的原因:
Component中的shouldComponentUpdate()
总是返回true。即使不写shouldComponentUpdate()默认返回值也是true
。
解决
方案一:手写阀门
shouldComponentUpdate()
可以接受两个参数:nextProps, nextState 即接下来要变化成的props和state。同时在shouldComponentUpdate()函数中可以获取当前的状态 this.state 和当前的属性 this.props
我们可以对比当前的状态和接下来要变化的状态
和 当前的属性和接下来要变化的属性
来决定 shouldComponentUpdate的返回值从而控制是否调用render。
eg:
import React, { Component } from 'react'
export default class Parent extends Component {
state = {carName:'奔驰c36'}
changeCar = ()=>{
this.setState({})
}
shouldComponentUpdate(nextProps,nextState) {
// console.log('当前', this.props, this.state);
// console.log('变化后',nextProps, nextState);
if(this.state.carName === nextState.carName){
return false
}else{
return true
}
}
render() {
console.log("父组件————render");
return (
<div style = {{width:'500px',background:'orange',padding:'8px'}}>
<span>{this.state.carName}</span><br/>
<button onClick = {this.changeCar}>点击换车</button>
<Child carName={this.state.carName}/>
</div>
)
}
}
class Child extends Component {
shouldComponentUpdate(nextProps,nextState) {
if(this.props.carName === nextProps.carName){
return false
}else{
return true
}
}
render() {
console.log("子组件————render");
return (
<div style={{width:'100%',background:'pink',padding:'8px'}}>
child组件
<span>父组件传过来的车: {this.props.carName}</span>
</div>
)
}
}
this.setState({})
传递的是空对象。不会触发状态改变,从而也不会触发子组件的重新渲染。
点击换车之后,控制台没有输出,即父子组件都没有重新渲染。
方案二:使用pureComponent
创建组件的时候直接使用 PureComponent
,就可以直接实现上述方案一中的逻辑,会自动在shouldComponentUpdate
中进行判断,减少不必要的render。
pureComponent的原理就是重写shouldComponentUpdate
import React, { PureComponent } from 'react'
export default class Parent extends PureComponent {
state = {carName:'奔驰c36'}
changeCar = ()=>{
this.setState({})
}
// shouldComponentUpdate(nextProps,nextState) {
// // console.log('当前', this.props, this.state);
// // console.log('变化后',nextProps, nextState);
// if(this.state.carName === nextState.carName){
// return false
// }else{
// return true
// }
// }
render() {
console.log("父组件————render");
return (
<div style = {{width:'500px',background:'orange',padding:'8px'}}>
<span>{this.state.carName}</span><br/>
<button onClick = {this.changeCar}>点击换车</button>
<Child carName={this.state.carName}/>
</div>
)
}
}
class Child extends PureComponent {
// shouldComponentUpdate(nextProps,nextState) {
// if(this.props.carName === nextProps.carName){
// return false
// }else{
// return true
// }
// }
render() {
console.log("子组件————render");
return (
<div style={{width:'100%',background:'pink',padding:'8px'}}>
child组件
<span>父组件传过来的车: {this.props.carName}</span>
</div>
)
}
}
注意:如果在使用PureComponent的时候,不能使用如下方式修改状态
changeCar = ()=>{
// this.setState({carName: "迈巴赫"})
// 使用这种方式更新,PureComponent会将obj对象和原state对象判断为一个对象(因为对象地址没变),那么shouldComponentUpdate直接返回false,不会进行状态更新
const obj = this.state
obj.carName = "迈巴赫"
this.setState(obj)
}
使用这种方式更新,PureComponent会将obj对象和原state对象判断为一个对象(因为PureComponent是浅对比,不会对比里面的数据具体发生了什么变化,所以PureComponent会判对为一个对象),那么PureComponent中重写的shouldComponentUpdate直接返回false,不会进行状态更新。
而用 this.setState({carName: "迈巴赫"})
传递的就是一个新对象,如果状态数据发生了改变,PureComponent会检测到,shouldComponentUpdate返回true。(在这种状况下,如果原state数据就是{carName: “迈巴赫”}, 使用 this.setState({carName: "迈巴赫"})
也能判断出数据没有修改 )
由于这个特性,所以注意当state或props数据如果是一个数组变化的时候尽量不要使用push()
、unshift()
这样的函数进行数组的修改因为数组地址没改变PureComponent不会检测到变化,shouldComponentUpdate就返回false,不能重新渲染。当数组变化的时候我们一般使用如下方法更新:
新数组=['添加的数据',...原数组]
renderProps
组件标签中的内容——childrenProps
当父组件调用子组件的时候子组件标签中的内容
会作为子组件上的this.props.children
属性传递给子组件。
eg:
import React, { Component } from 'react'
const MyContext = React.createContext()
export default class A extends Component {
state = {username:'yang', age:18}
render() {
const {username,age} = this.state
return (
<div style = {{width:'500px',background:'orange',padding:'8px'}}>
<div>A组件</div>
<h4>用户名:{username }</h4>
{/* 组件标签体中的内容作为props的children属性传递给B组件 */}
<B>标签中的内容</B>
</div>
)
}
}
class B extends Component {
render() {
return (
<div style={{width:'100%',background:'pink',padding:'8px'}}>
<div>B组件</div>
<h4>从A接受的用户名:{this.props.children}</h4>
</div>
)
}
}
输出
使用组件标签中的内容进行子组件展示
同理子组件标签同样可以包裹另一个组件标签,作为该子组件标签的子组件。
同样使用this.props.children
进行组件展示。
import React, { Component } from 'react'
const MyContext = React.createContext()
export default class A extends Component {
state = {username:'yang', age:18}
render() {
const {username,age} = this.state
return (
<div style = {{width:'500px',background:'orange',padding:'8px'}}>
<div>A组件</div>
<h4>用户名:{username }</h4>
{/* 组件标签体中的内容作为props的children属性传递给B组件 */}
<B>
<C/>
</B>
</div>
)
}
}
class B extends Component {
render() {
return (
<div style={{width:'100%',background:'pink',padding:'8px'}}>
<div>B组件</div>
<h4>从A接受的用户名:{this.props.children}</h4>
</div>
)
}
}
class C extends Component {
render() {
return (
<div style={{width:'100%',background:'yellow',padding:'8px'}}>
<div>C组件</div>
</div>
)
}
}
思考: 这种情况下如何将B组件中的属性传递给C组件呢?
不能传递,所以我们一般使用renderProps
使用组件标签中的render属性进行子组件展示——renderProps
使用方法 :
- 组件标签添加render属性(是一个回调函数),回调函数返回子组件
- 在组件中使用
this.props.render
进行属性展示
传递参数
- 在组件中将想要传递给子组件的参数放在
this.props.render()
中 - 在组件标签的render属性的回调函数中可以接收到在组件的
this.props.render()
中传递的数据,接收到之后就可以传递给子组件了。
import React, { Component } from 'react'
const MyContext = React.createContext()
export default class A extends Component {
state = {username:'yang', age:18}
render() {
const {username} = this.state
return (
<div style = {{width:'500px',background:'orange',padding:'8px'}}>
<div>A组件</div>
<h4>用户名:{username }</h4>
{/* 组件标签体中的内容作为props的children属性传递给B组件 */}
{/* <B>
<C/>
</B> */}
{/* render可以接受到从B组件的传递的this.props.render参数 */}
<B render={(name)=><C name={name}/>}/>
</div>
)
}
}
class B extends Component {
state = {name:'Tom'}
render() {
const {name} = this.state
return (
<div style={{width:'100%',background:'pink',padding:'8px'}}>
<div>B组件</div>
<h4>从A接受的用户名:
{/* render属性中内容展示位置 */}
{/* 将B组件的name传递给render中的回调函数 */}
{this.props.render(name)}
</h4>
</div>
)
}
}
class C extends Component {
render() {
return (
<div style={{width:'100%',background:'yellow',padding:'8px'}}>
<div>C组件:{this.props.name}</div>
</div>
)
}
}
错误边界
当组件发生错误的时候我们不希望他直接展示错误信息,而是展示错误提示页面,并且将该错误信息限制在该组件中不再向外扩散,这就是错误边界,我们一般向容易发生错误的组件的父组件
中做操作。
错误边界的使用:
-
使用
getDerivedStateFromError
函数,当子组件发生错误之后会触发父组件的getDerivedStateFromError函数
,并携带错误信息 ,我们一般在该函数中做相应的错误处理。
getDerivedStateFromError的特点:只能捕获后代组件生命周期
(即render函数中)产生的错误,不能捕获后代组件非render函数中的错误 ,也不能捕获自己组件产生的错误。 -
使用
componentDidCatch
函数,当当前组件渲染出错的时候就会调用该钩子
eg:
Parent组件
import React, { Component } from 'react'
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
state ={
hasError:''//表示子组件是否产生错误
}
// 当Praent的子组件发生错误的时候会触发getDerivedStateFromError函数,并携带错误信息
static getDerivedStateFromError(error){
console.log("粗错了", error);
// 直接修改estate中的信息
return {hasError:error}
}
// 当组件渲染出错的时候就会调用该钩子
componentDidCatch(){
console.log('统计错误次数,发送给后台,用于编码人员进行问题解决');
}
render() {
return (
<div style = {{width:'500px',background:'orange',padding:'8px'}}>
<h2>Parent组件</h2>
{
this.state.hasError? <h2>当前网络不稳定,请稍后重试</h2>:<Child/>
}
</div>
)
}
}
Children组件
import React, { Component } from 'react'
export default class Child extends Component {
state = {
// users:[
// {id:'001', name:'tom', age:18},
// {id:'002', name:'jack', age:20},
// {id:'003', name:'yang', age:18}
// ]
users:'test-error'
}
render() {
const {users} = this.state
return (
<div style = {{width:'300px',background:'pink',padding:'8px'}}>
<h2>Child组件</h2>
{
users.map((userObj)=>{
return <h4 key={userObj.id}>{userObj.name}---{userObj.age}</h4>
})
}
</div>
)
}
}
当children组件出错,显示如下内容:
组件通信方式总结
组件间的关系
- 父子组件
- 兄弟组件
- 祖孙组件
组件间通信方式
- props通信 —— 父子组件间的通信
- childrenProps
- renderProps
-
消息订阅与发布 —— 所有组件间
第三方库:pubs-sub -
集中式管理
第三方库:redux —— 所有组件间 -
conText —— 祖孙之间
生产者消费者模式(Provider)
比较好的搭配模式
父子组件——props通信
兄弟组件——消息订阅与发布、集中式管理
祖孙之间——消息订阅与发布、集中式管理、conText