笔记来源
开始 – React (Q1-2,有点傻的问题已经删掉了)
尚硅谷React教程(2022加更,B站超火react教程)_哔哩哔哩_bilibili (Q3-)
Q3:jsx语法规则详细
Q4:react创建虚拟dom的两种方式
A4:jsx和React.createElement()
Q5:真实dom与虚拟dom是什么与其的区别
是什么?virtual dom本质是js对象形式对dom的描述,而页面渲染出的每一个节点则是一个Real dom。
区别1:虚拟dom不会进行排版和重绘操作,而真实dom会频繁重排与重绘。
区别2:虚拟dom的总消耗是“虚拟dom增删改 + 真实dom差异增删改 + 排版与重绘”,真实dom的总消耗是“真实dom的完全增删改 + 排版与重绘”
本题题解:https://github.com/febobo/web-interview/issues/181
Q6:react类组件中this的指向问题?构造函数/render方法/自定义方法
构造函数一定指向类实例本身
render方法是react new出实例后调用,因此this为类实例本身
类中自定义方法this指向,如下changeWeather中this为undefined
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Weather extends React.Component{ constructor(props){ super(props) this.state = {isHot :false} }
render() { return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1> }
changeWeather(){ console.log(this) } }
|
Q7:react类组件自定义的方法中的this为undefined,如何解决?两种解决方案如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Weather extends React.Component{ constructor(props){ super(props) this.state = {isHot :false} this.changeWeather = this.changeWeather.bind(this) }
render() { return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1> }
changeWeather(){ console.log(this) } }
class Weather extends React.Component{ constructor(props){ super(props) this.state = {isHot :false} }
render(){ return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1> }
changeWeather = () => { console.log(this) } }
|
Q8:什么是state? 如何在类组件中使用state?
A8,1什么是state?
state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合)
组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
1 2 3
|
this.setState({xxx:!xxx})
|
Q9:精简类组件?干掉constructor
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Weather extends React.Component{ state = {isHot :false} render() { return <h1 onClick={this.changeWeather}>今天天气很{this.state ? "炎热" : "凉爽"}</h1> } changeWeather = () => { console.log(this) } }
|
Q10
什么是props?
在类组件中props的使用?
对props的一些限制?
props简写方式?
类构造函数中props的使用?
函数组件中props的使用?
A10,1 什么是props?
每个组件对象都有props(properties的简写)属性
组件标签所有属性都保存在props中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| class Person extends React.Component{ render() { const {name,sex,age} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } changeWeather = () => { console.log(this) } }
ReactDOM.render(<Person name="whh" age={18} sex="男"/>,document.querySelector(".test"))
const p1 = {name:"whs",age:18,sex:"男"} ReactDOM.render(<Person {...p}/>,document.querySelector(".test1"))
Person.propTypes = { name:PropTypes.string.isRequired, sex:PropTypes.string, age:PropTypes.number speak:PropTypes.func }
Person.defaultProps = { sex:"男" age:18 }
class Person extends React.Component{ static propTypes = { name:PropTypes.string.isRequired, sex:PropTypes.string, age:PropTypes.number speak:PropTypes.func } static defaultProps = { sex:"男" age:18 } render() { const {name,sex,age} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } changeWeather = () => { console.log(this) } }
class Person extends React.Component{ constructor(props){ console.log(this.props) } static propTypes = { name:PropTypes.string.isRequired, sex:PropTypes.string, age:PropTypes.number speak:PropTypes.func } static defaultProps = { sex:"男" age:18 } render() { const {name,sex,age} = this.props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) } changeWeather = () => { console.log(this) } }
function Person(props){ const {name,sex,age} = props return ( <ul> <li>姓名:{name}</li> <li>性别:{sex}</li> <li>年龄:{age}</li> </ul> ) }
Person.propTypes = { name:PropTypes.string.isRequired, sex:PropTypes.string, age:PropTypes.number speak:PropTypes.func }
Person.defaultProps = { sex:"男" age:18 }
ReactDOM.render(<Person name="whh" age={18} sex="男"/>,document.querySelector(".test"))
|
Q11:ref的三种创建方法
string绑定在标签
回调Refs (内联与类的绑定函数)
React.createRef()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| class Demo extends React.Component { showData = () => { console.log(this.refs.input); };
render(): React.ReactNode { return ( <div> <input ref="input" type="text" placeholder="点击按钮提示数据"></input> <button onClick={this.showData}>点我提示左侧数据</button> </div> ); } }
class Demo extends React.Component { showData = () => { const {input} = this console.log(input) };
render(): React.ReactNode { return ( <div> <input ref={(currectNode) => {this.input = currectNode}} type="text" placeholder="点击按钮提示数据"></input> <button onClick={this.showData}>点我提示左侧数据</button> </div> ); } }
class Demo extends React.Component { showData = () => { const {input} = this console.log(input) }; savaInput = (c) => { this.input = c console.log(c) }
render(): React.ReactNode { return ( <div> <input ref={this.savaInput} type="text" placeholder="点击按钮提示数据"></input> <button onClick={this.showData}>点我提示左侧数据</button> </div> ); } }
class Demo extends React.Component { myRef = React.creatRef() showData = () => { const {input} = this console.log(input) }; savaInput = (c) => { this.input = c console.log(c) }
render(): React.ReactNode { return ( <div> <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"></input> <button onClick={this.showData}>点我提示左侧数据</button> </div> ); } }
|
Q12:非受控组件与受控组件的理解?
非受控组件,一般是用ref实现,当用户提交表单时再去获取内容(现用现取节点值,不受我们监听state状态)
受控组件,一般用onChange来实现的,当用户改变input内容的时候自动调用onChange的回调然后去改变state中的状态(类似于vue中的双向绑定)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| class Login extends React.Component { handleSubmit = (event) => { const { username, password } = this; if (username != null && password != null) { alert( `你输入的用户名是:${username.value}},你输入的密码是:${password.value}` ); } };
render(): React.ReactNode { return ( <form onSubmit={this.handleSubmit}> <input ref={(c) => (this.username = c)} type="text" name="username" /> <input ref={(c) => (this.password = c)} type="password" name="password" /> <button>登录</button> </form> ); } }
class Register extends React.Component { state = { username: "", password: "" };
saveUsername = (event: any) => { this.setState({ username: event.value }); };
savePassword = (event: any) => { this.setState({ username: event.value }); };
handleSubmit = (event: any) => { const { username, password } = this.state; alert(`你输入的用户名是:${username},你输入的密码是:${password}`); };
render(): React.ReactNode { return ( <form onSubmit={this.handleSubmit}> <input onChange={this.saveUsername} type="text" name="username" /> <input onChange={this.savePassword} type="password" name="password" /> <button>登录</button> </form> ); } }
|
Q13:什么是高阶函数与函数柯里化?在react中的使用场景?
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
若A函数,接收的参数是一个函数,那么A就可以称为高阶函数
若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数
常用的高阶函数有:Promise、setTimeout、数组的方法
函数柯里化:通过调用 继续返回函数 的方式,实现多次接受参数最后统一处理的函数编码方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
function sum (a) { return (b) => { return (c) => { return a+b+c } } }
const result = sum(1)(2)(3)
class Register extends React.Component { state = { username: "", password: "" };
saveUsername = (event: any) => { this.setState({ username: event.value }); };
savePassword = (event: any) => { this.setState({ username: event.value }); };
saveData = (dataType:string) => { return (event:any) => { this.setState([dataType],event.target.value) } }
handleSubmit = (event: any) => { const { username, password } = this.state; alert(`你输入的用户名是:${username},你输入的密码是:${password}`); };
render(): React.ReactNode { return ( <form onSubmit={this.handleSubmit}> <input onChange={this.saveData("username")} type="text" name="username" /> <input onChange={this.saveData("password")} type="password" name="password" /> <button>登录</button> </form> ); } }
|
Q14:不用高阶函数和函数柯里化也能接受到监听事件中传入的dataType和event(如下列代码所示)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class Register extends React.Component { state = { username: "", password: "" };
saveUsername = (event) => { this.setState({ username: event.value }); };
savePassword = (event) => { this.setState({ username: event.value }); };
saveData = (dataType,event) => { this.setState([dataType],event.target.value) }
handleSubmit = (event) => { const { username, password } = this.state; alert(`你输入的用户名是:${username},你输入的密码是:${password}`); };
render(): React.ReactNode { return ( <form onSubmit={this.handleSubmit}> <input onChange={(event) => {this.saveData("username",event)}} type="text" name="username" /> <input onChange={(event) => {this.saveData("password",event)}} type="password" name="password" /> <button>登录</button> </form> ); } }
|
Q15:旧生命周期钩子流程介绍?(React16.x以及之前)
初始化阶段:由ReactDOM.render()触发 –初次渲染
- constructor()
- componentWillMount()
- render()
- componentDidMount() –>常用 (一般在这个钩子中做一些初始化的工作,例如:开启定时器、发送网络请求、订阅消息)
更新阶段:由组件内部this.setState()或父组件render触发
- shouldComponentUpdate()
- componentWillUpdate()
- render() –>必须调用一个
卸载组件:由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount() –>常用 (一般在这个钩子中做一些收尾的工作,例如:关闭定时器、取消订阅消息)
Q16:新生命钩子的流程介绍?(React17.x-至今)
初始化阶段:由ReactDOM.render()触发 –初次渲染
- constructor()
- getDerivedStateFromProps
- render()
- componentDidMount() –>常用 (一般在这个钩子中做一些初始化的工作,例如:开启定时器、发送网络请求、订阅消息)
更新阶段:由组件内部this.setState()或父组件render触发
- getDerivedStateFromProps
- shouldComponentUpdate()
- render() –>必须调用一个
- getSnapshotBeforeUpdate
- componentDidUpdate()
卸载组件:由ReactDOM.unmountComponentAtNode()触发
- componentWillUnmount()
Q17
react/vue中的key有什么作用?(key的内部原理是什么)
为什么遍历列表时,key最好不要用index?
虚拟DOM中key的作用:
- 简单的说:key是虚拟DOM对象的表示,在更新显示时key起着至关重要的作用
- 详细的说:当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】和【旧虚拟DOM】的diff比较,比较规则如下:
- 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没有变,则直接用之前的真实DOM
- 若虚拟DOM中内容变了,则生成新的真实DOM,替换掉页面之前的真实DOM
- 旧虚拟DOM未找到与新虚拟DOM相同的key,根据数据创建新的真实DOM,随后渲染到页面
用index作为key可能会引发的问题:
- 若对数据进行:逆向添加,删除等破坏规则操作:会产生没有必要的真实DOM的更新 –> 虽然界面没问题,但效率低
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 –> 界面有问题
- 注意!如果不存在对数据的逆向添加、逆向删除等破坏顺序操作。仅用了渲染列表用于展示,使用index作为key是没有问题的
Q18:todoList案例相关知识点
- 拆分组件、实现静态组件,注意className、style的写法
- 动态初始化列表、如何确定将数据放在哪个组件的state中?
- 某个组件使用:放在其自身的state中
- 某些组件使用:放在他们的共同的父组件state中(官方称为:状态提升)
- 关于父子之间通信:
- 【父组件】给【子组件】传递数据:通过props传递
- 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
- 注意 defaultChecked 和 checked 的区别,类似还有:defaultValue 和 value
- 状态在哪里,操作状态的方法就在哪里
Q19:github搜索案例涉及知识点
设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办
ES6小知识点:解构赋值 + 重命名
1 2 3 4 5 6 7
| let obj = {a:{b:1}}
const {a} = obj
const {a:{b}} = obj
const {a:{b:value}} = obj
|
消息订阅与发布机制
- 先订阅,再发布
- 适用于任何组件通信
- 要在组件中componentWillUnmount中取消订阅
fetch发送请求(关注分离的设计思想)
Q20:前端路由基本原理
如果想在原生中自己封装一个路由库可以基于history封装好的基石(去bootcdn上搜索history)
方法一:直接使用h5推出的history身上的api
方法二:hash值(瞄点)路径带#(兼容性强)
Q21:react路由的基本使用
温馨提示:目前尚硅谷教的是5版本 npm i react-router-dom@5
1.明确好界面导航区和展示区
2.导航区的a标签改为Link标签
3.展示区些Route标签进行路径的匹配
1
| <Route path='/xxx' component={Demo}/>
|
4.<App>的最外侧包裹了一个<BrowserRouter>或者<HashRouter>
Q22:路由组件与一般组件
写法不同
- 一般组件:
<Demo />
- 路由组件:
<Route path="/demo" component={Demo} />
存放位置不同
- 一般组件:component
- 路由组件:pages
接收的props不同
- 一般组件:写组件标签时传递了什么,就能收到什么
- 路由组件:接受3个固定的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| history: go: f go(n) goBack:f goBack() goForward:f goForward() push:f push(path,state) replace:f replace(path,sstate)
Location: pathname:"/about" search:"" state:undefined
match: params:{} path:"/about" url:"/about"
|
Q23:NavLink与封装NavLink
NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
标签内容是一个特殊属性(children)
通过this.props.children可以获取标签体中的内容
Q24: react解决样式丢失问题
- 在public下的html中解决
1 2 3 4 5 6
| <link href="./css/bootstrap.css" rel="stylesheet">
<link href="/css/bootstrap.css" rel="stylesheet">
<link href="%PUBLIC_URL%/css/bootstrap.css" rel="stylesheet">
|
- 使用router的HashRouter解决,因为#后的网络访问时认为是前端资源,因此不会将前端路由带上。
Q25:react路由的严格匹配和模糊匹配
默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
开启严格匹配
1
| <Route exact={true} path="/about" component={About}/>
|
- 严格模式不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
Q26:路由重定向的使用
1 2 3 4 5 6
| <Switch> <Route path="/about" component={About} /> <Route path="/home" component={Home} /> <Redirect to="/about" /> </Switch>
|
Q27:嵌套路由
注册子路由时要写上父路由的path值
路由的匹配是按照注册路由的顺序进行的
Q28:向路由组件传递参数(使用优先级从上到下)
- params参数
1 2 3 4 5
| <Link to="/demo/test/tom/18">详细</Link>
<Route path="/demo/test/:name/:age" component={Test} />
|
- search参数
1 2 3 4 5 6
| <Link to="/demo/test?name='tom'&age=18">详细</Link>
<Route path="/demo/test" component={Test} />
|
- state参数(需要处理保密数据用)
1 2 3 4 5 6
| <Link to={{path:'/demo/test',state:{name:'tom',age:18}}}>详细</Link>
<Route path="/demo/test" component={Test} />
|
Q29:编程式路由导航
借助this.props.history对象上的api对操作路由跳转、前进、后退
this.props.history.push()
this.props.history.replace()
this.props.history.goBack()
this.props.histroy.goForward()
this.props.history.go()
Q30:withRouter的使用
withRouter可以加工一般组件,让一般组件具备由组件所特有的api
withRouter的返回值是一个新组件
Q31:BrowserRouter和HashRouter的区别
底层原理不一样
- BrowserRouter使用的是H5的history api,不兼容ie9及以下版本
- HashRouter使用的是url的哈希值
url表现形式不一样
- BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
- HashRouter的路径中包含#,例如localhost:3000/#/demo/test
刷新后对路由state参数的影响
- BrowserRouter没有任何影响,因为state保存在history对象中
- HashRouter刷新后会导致由state参数的丢失
备注:HashRouter可以用于解决一些路径错误相关的问题
Q32:UI组件库
国内使用多的antd
官方文档:https://ant.design/docs/react/introduce-cn
主要掌握:按需引入和自定义主题
国外使用多material ui
官方文档:https://material.io/
Q33:求和案例_redux精简版
去除Count组件自身状态
src下建立:
- -redux
- store.js
- count_reducer.js
store.js
- 引入redux的createStore函数,创建一个store
- createStore调用时要传入一个为其服务的reducer
- 暴露store给你的组件使用
count_reducer.js
- reducer的本质是一个函数,接收:preState,action,返回加工后的状态
- reducer有两个作用:初始化状态,加工状态
- reducer被第一次调用时,是store自动触发的,传递的preState是undefined
在index.js中检测store中状态的改变,一旦发生改变重新渲染
- 备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写
Q34:求和案例_redux异步action版
明确:延迟的动作不想交给组件自身,想交给action
何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
具体编码:
- npm i redux-thunk,并配置在store中
- 创建action的函数不再返回一个一般对象,而是一个正在的函数,该函数中写异步任务
- 异步任务有结果后,分发一个同步的action去真正操作数据
备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步的action
Q35:react-redux
使用目的是讲react组件和redux api分开

明确两个概念:
- UI组件:不能使用任何的redux的api,只负责页面的呈现和交互等
- 容器组件:负责和redux通信,将结果交给UI组件
如何创建一个容器组件 - 靠react-redux的connect函数
- connect(mapStateToProps.mapDispatchToProps)(组件)
- -mapStateToProps:映射状态,返回的是一个对象
- -mapDispatchToProps:映射操作状态的方法,返回值是一个对象
备注:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入
Q36:求和案例-react-redux优化
容器组件和UI组件合并为一个文件
无需自己给容器组件传递store,给<App/>包裹一个 <Provider store={store}></Provider>即可
使用react-redux后不再需要自己去检测redux状态的变化,容器组件自动帮你完成这个工作
mapDispatchToProps也可以简单的写成一个对象
一个组件要和redux”打交道”要经过那几步?
定义好UI组件–不要暴露
引入react-redux的connect方法生成一个容器组件,并暴露,写法如下:
1 2 3 4 5
| connect((state) => ({ count: state }), { jia: createIncrementAction, jian: createDecrementAction, jiaAsync: createIncrementAsyncAction, })(Count);
|
在UI组件中通过this.props.xxxx读取和操作状态
Q37:求和案例-react_redux数据共享版
定义一个Person组件,和Count组件通过redux共享数据
为Person组件编写:reducer、action,配置constant常量
重点:Person的reducer和Count的Reducer要使用combineReducer进行合并,合并是重点
交给store的是总reducer,最后注意在组件中取出状态的时候,记得要“取到位”
Q38:纯函数
一类特别的函数:只要是同样的输入(实参),必须得到同样的输出(返回)
必须遵从以下一些约束
- 不得改写参数数据
- 不会产生任何副作用,例如网络请求,输入和输出设备
- 不能调用
Date.now()或者Math.random()等不纯的方法
redux的reducer函数必须是一个纯函数
Q39:求和案例_react_redux开发者工具的使用
npm i redux-devtools-extension
.store中进行配置
import { composeWithDevTools } from "redux-devtools-extension";
createStore(reducers,composeWithDevTools(applyMiddleware(thunk)));
Q40:求和案例_react_redux最终版
所有变量名字要规范,尽量触发对象的简写形式
reducers文件中,编写index.js专门用于汇总并暴露所有的reducer
Q41:Hooks
- React Hook/Hooks是什么?
1 2
| Hook是React 16.8.0版本新增的新特性/新语法 可以让你在函数组件中使用state以及其他React特性
|
- 三个常用的Hook
1 2 3
| State Hook:React.useState() Effect Hook:React.useEffect() Ref Hook:React.useRef()
|
- State Hook
1 2 3 4 5 6 7 8
| (1)State Hook让函数组件也可以有state状态,并进行状态数据的读写操作 (2)语法:const [xxx,setXxx] = React.useState(initValue) (3)useState()说明: 参数:第一次初始化指定的值在内部作缓存 返回值:包含2个元素的数组,第一个内部当前状态值,第二个为更新状态的函数 (4)setXxx()2种用法 setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值 setXxx(value => newValue):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
|
- Effect Hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| (1)Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子) (2)React中的副作用操作: 发ajax请求数据获取 设置订阅 / 启动定时器 手动更改真实DOM (3)语法和说明 useEffect(()=>{ //在此可以执行任何带副作用的操作 return ()=>{ //在此像一些收尾工作,比如清楚定时器/取消订阅等 } },[stateValue])//如果指定的是[],回调函数只会在第一次render()后执行 (4)可以把 useEffect Hook看做如下三个函数的组合 componentDidMount() componentDidUpdate() componentWillUnmount()
|
- Ref Hook
1 2 3
| (1)Ref Hook可以在函数组件中存储/查找组件内的标签或任意其他数据 (2)语法:const refContainer = useRef() (3)作用:保存标签对象,功能与React.createRef()一样
|
Q42:Fragment
使用
1 2
| <Fragment></Fragment> <></>
|
Q43:Context
理解:一种用于组件间通信的方式,常用于【祖组件】与【后代组件】间通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| (1)创建Context容器对象 const XxxContext = React.createContext() (2)渲染子组件时,外面包裹xxxContext.Provider,通过value属性给后代组件传递数据: <xxxContext.Provide value={数据}> 子组件 </xxxContext.Provide> (3)后代组件读取数据: //第一种方式:使用类组件 static conTextType = xxxContext //声明接收context this.context //读取context中的value数据 //第二种方式:函数组件与类组件都可以使用(不是特别理解这种写法) <xxxContext.Consumer> { value => { //value就是context中的value数据 要显示的内容 } } </xxxContext.Consumer>
|
Q44:组件优化
Component的两个问题
只用执行setState(),即便不改变状态数据,组件也会重新render() ==> 效率低
只要当前组件重新render(),就会自动重新render子组件 ==> 效率低
效率高的做法
只有当前组件的state或者props发生改变的时候才重新render()
原因
Component中的shouldComponentUpdate()总是返回true
解决
1 2 3 4 5 6 7 8 9 10
| 办法1 重写shouldComponentUpdate()方法 比较新旧state或props数据,如果有变化才返回true,如果没有返回false 办法2 使用PureComponent PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true 注意: 只是进行state和props数据的浅比较,如果只是数据对象内部数据变化了,返回false 不要直接修改state数据,而是要产生新数据 项目中一般使用PureComponent来优化
|
Q45:render props
如何向组件内部动态传入带内容的结构(标签)?
1 2 3 4 5
| Vue中: 使用slot技术,也就是通过组件标签传入结构 <A><B/></A> React中: 使用children props:通过组件标签传入结构 使用render props:通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
|
children props
1 2 3 4 5
| <A> <B>xxx</B> </A> {this.props.children} 问题:如果B组件需要A组件内的数据 ==> 找不到
|
render props
1 2 3
| <A render={(data)=><C data={data}></C>}></A> A组件:{this.props.render(内部state数据)} C组件:读取A组件传入的数据展示 {this.props.data}
|
Q46:错误边界
理解:用来捕获后代组件错误,渲染出备用页面
特点:只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件,定时器中产生的错误
使用方式:
getDerivedStateFromError配合componentDidCatch
1 2 3 4 5 6 7 8 9
| static getDerivedStateFromError(error){ return {hasError:true} }
componentDidCatch(error,info){ console.log(error,info) }
|
Q47:组件通信方式
组件间的关系
父子组件
兄弟组件(非嵌套组件)
祖孙组件(跨级组件)
几种通信方式
1 2 3 4 5 6 7 8 9
| (1)props: a.children props b.render props (2)消息订阅与发布 pubs-sub、event等 (3)集中式管理 redux、dva (4)conText 生产者-消费者模式
|
比较好的搭配方式
1 2 3
| 父子组件:props 兄弟组件:消息订阅-发布、集中式管理 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发中使用比较少,封装组件使用多)
|