知识点总结 REACT
1、react中如何创建一个组件
ES6:class 组件名 extends Component{}
ES5:var App=React.createClass({})
2、render函数什么时候会执行
当this.state或者this.props发生改变的时候render函数执行
3、react中如何对state中的数据进行修改?setState为什么是异步的
修改数据通过this.setState(参数一,参数二)
this.setState是一个异步函数
参数一:需要修改的数据,对象的形式
参数二:修改成功的回调函数,这里相当于componentDidMount,可以获取到数据更新后的DOM结构
this.setState中的第一个参数除了可以写成一个对象以外,还可以写成一个函数,函数中第一个值为prevState,第二个值为prePprops this.setState((prevState,prop)=>{})
为什么setState是异步的:
当批量执行state的时候可以让DOM渲染的更快,也就是说多个setState在执行的过程中还需要被合并。
4、react中如何定义自定义属性,以及限制外部数据的类型
自定义属性:
组件名.defaultProps={
key:val
}
限制外部数据的类型:
①引入propTypes第三方模块
②类型限制
组件名.propTypes={
key:propTypes.类型
}
5、react路由常用的组件配置项
BrowserRouter 路由的根组件,根组件下面只能有一个子元素
HashRouter 路由的根组件,根组件下面只能有一个子元素
withRouter
Route 路由路径对应的文件
Link 进行路由的跳转
NavLink 进行路由的跳转,选中标签的时候NavLink会加上一个class
Switch 被Switch包裹的Route渲染的时候只会渲染一个路径,建议:子页面不要放在Switch中,主页面放在Switch中
Redirect 路由重定向
6、react路由中Route渲染组件的方法有哪几种?区别是什么
①<Route path="/home" component={组件名}></Route>
通过component进行组件的渲染,这种方式的优点在于可以直接在组件的内部接收到history、location、match,缺点在于如果需要传参的组件,或者渲染jsx语法的时候无法使用。
②<Route path="/home" render={()=>{
return <Home/>
}}></Route>
通过render进行渲染组件,优点在于可以进行组件传参,还可以渲染非组件的标签,缺点在于如果需要使用history、location、match的话需要在函数中传递过去。
7、如何控制路由路径的完全匹配
给NavLink或Route设置exact属性
8、react中路由传参的方法有哪些
//方案一:通过params进行传值 <Router path="/one/:id" render={(history)=>{ return <One history={history} /> }}> </Router> //html跳转 <NavLink to={"/one/"+1}>one</NavLink> //js跳转 this.props.history.push("/one/"+"1") //接收值:在组件内部通过constructor接收 constructor({history,location,match}){ super(); console.log(match.params) } //方案二:通过query进行传值 <Router path="/one" render={(history)=>{ return <One history={history} /> }}></Router> //html跳转 <NavLink to={ pathname:"/one" query:{data:1} }>one</NavLink> //js跳转 this.props.history.push({pathname:"/one",query:{data:1}}) //获取值 this.props.location.query.data 注意:如果通过render进行渲染的时候需要在回调中将history的值传递过去,
如果是component进行渲染的话,只需要通过this.props进行接收
9、react中的编程式导航有哪些
this.props.history.push("路径") 跳转路径
this.props.history.back() 返回
this.props.history.forward() 前进
this.props.history.replace() 替换
10、react的生命周期
组件创建的过程:
constructor
componentWillMount
render
componentDidMount
当props中的数据发生改变后会执行哪些生命周期函数:
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
组件销毁的时候:
componentWillUnmount
11、react中如何强制更新DOM
this.foreUpdate()
12、谈谈对react中生命周期shouldComponentUpdate的理解
shouldComponentUpdate是react性能优化非常重要的一环。组件接收新的state或者props时调用,我们设置在此对比前后两个props和state是否相同,如果相同则返回false阻止更新,因为相同的属性状态一定会产生相同的DOM树,这样就不需要创造新的DOM树和旧的DOM树进行diff算法对比,节省大量性能,尤其是在DOM结构复杂的时候。
13、谈谈你对虚拟DOM的理解,以及好处
虚拟DOM:真实的js对象
好处:虚拟DOM提高了react性能,每次更新数据后都会重新计算上虚拟DOM,并和上一次的虚拟DOM进行对比,对发生变化的部分进行批量更新。react中也提供了shouldComponentUpdate声明周期的回调,来减少数据变化后不必要的虚拟DOM对比过程,保证性能。
14、谈谈对flux的理解
利用单项数据流的方式来组合react的视图组件,它是一种模式,而不是一种框架。
flux可以帮我们解决非父子组件之间的传值
flux数据传递流程
https://www.cnblogs.com/nanianqiming/p/9870194.html
15、react17版本废除的生命周期,与新增的生命周期函数
由于未来采用异步渲染机制,所以即将在17版本中去掉的生命周期钩子函数:
componentWillMount
componentWillReceiveProps
componentWillUpdate
新增的生命周期:
static getDeriveStateFromProps(nextProps,prevState){}
用于替换componentWillReceiveProps,可以用来控制props更新state的过程,它返回一个对象表示新的state,如果不需要更新,返回null即可。在每次渲染之前都会调用,不管初始挂载还是后面的更新都会调用,这一点和componentWillReceiveProps(只有当父组件造成重新渲染时才调用)不同,每次都应该返回新对象。
getSnapshotBeforeUpdate(){}
用于替换componentWillUpdate,该函数会在update后DOM更新前被调用,用于读取最新的DOM数据,返回值将作为componentDidUpdate的第三个参数,在最新的渲染数据提交给DOM前会立即调用,它让你在组件的数据可能要改变之前调用他们。
componentDidCatch(error,info){}
如果一个组件定义了componentDidCatch生命周期,则它将成为一个错误边界。错误边界会捕捉渲染期间,在生命周期中和在它们之下整棵树的构造函数中的错误,就像使用了try catch,不会将错误直接抛出了,保证应用的可能性。
16、this.setState之后做了哪些操作
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
17、当组件的key值发生改变后会执行哪些生命周期函数
componentWillUnmount
constructor
componentWillMount
render
componentDidMount
18、在react中是否封装过组件?封装组件要注意的地方
常用封装组件用到的东西
propTypes 限制外部数据的类型
defaultProps 设置默认的外部数据
父传子 子传父 context等传值方式
高阶组件
封装组价要注意组件的复用性、灵活性
19、如何接收组件内部的标签/内容
this.props.children
20、说一下对redux的理解,以及使用的方式
其实redux就是Flux的一种进阶实现,它是一个应用数据流框架,主要作用是应用状态的管理。
redux的三大原则:
①单一的数据源
②state是只读的
③使用纯函数来进行修改
redux中常用的三个方法:
①createStore() 创建store
②combineReducers() 合并多个reducer
③applyMiddleware() 使用中间件,处理异步的action
redux数据传递的流程:
当组件需要改变store的时候,需要创建一个action,然后通过dispatch(action)传递给store,然后store把action转发给reducers。reducers会拿到previousState(以前的state数据)和action。然后将previousState和action进行结合做新的数据(store)修改。然后生成一个新的数据传递给store。store通过触发subscribe方法来调用函数执行setState使得view的视图发生改变。
21、请说下在react中如何处理异步的action
通过applyMiddleware来使用中间件来处理action
常用的中间件:
redux-promise-middleware
redux-thunk
redux-saga
22、请说下对redux中间件的理解
中间件:请求和回复之间的一个应用
在redux中,中间件是dispatch和reducer之间的一个应用
23、请说下redux中你对reducers的理解,以及如何合并多个reducers
在redux中reducer是一个纯函数,其中这个纯函数会接收2个参数,一个是state,一个是action。state用来保存公共的状态,state的特点是只能读不能修改。
在实际开发中因为会涉及到多人协作开发,所以每个模块都有一个reducer,我们可以通过combineReducers来合并多个reducers。
24、请说下对高阶组件的理解,以及作用
高阶组件:它是一个函数,接收一个组件作为参数,返回一个相对增强性的组件。
高阶组件是一个函数,并不是组件。
作用:
①属性代理----主要进行组件的复用(最常见的高阶组件的使用方式,通过做一些操作,将被包裹组件的props和新生成的props一起传递给此组件)
②反向继承----主要进行渲染的劫持(返回的react组件继承了被传入的组件,所以它能访问到的区域、权限更多,想比属性代理的方式,它更像是打入组件内部,对其进行修改)
25、在react中如何解决单页面开发首次加载白屏现象
①通过路由懒加载的方式 react-loadable
②首屏服务端渲染
26、请说下react中key值的理解
react利用key来识别组件,它是一种身份标识,相同的key值react认为是同一个组件,这样后续相同的key对应的组件都不会被创建。
有了key属性后,就可以与组件建立一种对应关系,react根据key来决定是销毁重新创建组件还是更新组件。
key相同,若组件属性有所变化,则react只更新组件对应的属性,没有变化则不更新。
key值不同,则react先销毁该组件(有状态组件的componentWillUnmount会执行),然后重新创建该组件(有状态组件的constructor和componentWIllUnmount都会执行)。
27、组件第一次执行的时候会执行哪些生命周期函数
constructor
componentWillMount
render
componentDidMount
28、哪些声明周期会执行多次
render
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
29、当this.state和this.props执行的时候会执行哪些生命周期函数
this.state
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
this.props
componentWillReceiveProps
shouldComponentUpdate
render
componentDidUpdate
30、谈谈对context的理解
当不想在组件树中通过逐层传递props或state的方式来传递数据时,可以使用context来实现跨层级的组件数据传递,使用context可以实现跨组件传递。
旧版context的基本使用:
①getChildContext根组件中声明,一个函数,返回一个对象,就是context
②childContextTypes根组件中声明,指定context的结构类型,如不指定会产生错误
③contextTypes子孙组件中声明,指定要接收的context的结构类型,可以只是context的一部分结构。contextTypes没有定义,context将是一个空对象。
④this.context在子孙组件中通过此来获取上下文
新版context的使用:
①根组件中引入GlobalContext,并使用GlobalContext.Provider(生产者)
②组件引入GlobalContext并调用context,使用GlobalContext.Consumer
31、谈谈对react中withRouter的理解
默认情况下必须是经过路由匹配渲染的组件在this.props上拥有路由参数,才能使用编程式导航的写法,withRouter是把不通过路由切换过来的组件,将react-router的history、location、match三个对象传入props对象上。
简单来说就是:不是路由包裹的组件也可以使用路由的一些方法
32、说说对puerComponent的理解
puerComponent表示一个纯组件,可以用来优化react程序,减少render函数渲染的次数,提高性能。
puerComponent进行的是浅比较,也就是说如果是引用数据类型的数据,只会比较不是同一个地址,而不会比较这个地址里的数据是否一致。
好处:当组件更新时,如果组件的props或state都没有改变,render函数就不会触发,省去虚拟DOM的生成和对比过程,达到提升性能的目的。具体原因是react自动帮我们做了一层浅比较。
33、react请求接口数据是在componentDidMount还是componentWillMount周期好
如果你要获取外部数据并加载到组件上,只能在组件“已经”挂载到真是的网页上才能做这件事,其他情况你是加载不到组件的。componentDidMount方法中的代码,是在组件已经完全挂载到网页上才会调用被执行,所以保证数据的加载。
react异步渲染开启的时候,componentWillMount就可能中途被打断,中断之后渲染又要重新做一遍,如果在componentWillMount中做ajax调用,代码里看到只有调用一次,但是实际上可能调用n多次,这个浪费了性能。如果把ajax放在componentDidMount中,因为componentDidMount在第二阶段,所以绝对不会多次重复调用,这里是ajax合适的位置。
34、react性能优化
一、immutable简介
Immutable Data是一旦创建,就不能修改的数据。对Immutable对象的任何修改或添加删除操作都会返回一个新的Immutable对象。Immutable实现的原理是Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变,同时为了避免deepCopy把所有的节点都复制一遍带来的性能损耗。
在js中,引用类型的数据,优点在于频繁的操作数据都是在原对象的基础上修改,不会创建新对象,从而可以有效的利用内存,不会浪费内存,这种特性称为mutable(可变),但恰恰它的优点也是它的缺点,太过于灵活多变在复杂数据的场景下也造成了它的不可控性,假设一个对象在多处用到,在某一处不小心修改了数据,其他地方很难预见到数据是如何改变的,针对这种问题的解决方法,一般就像刚才的例子,会想复制一个新对象,再在新对象上做修改,这无疑会造成更多的性能问题以及内存浪费。
为了解决这种问题,出现了immutable对象,每次修改immutable对象都会创建一个新的不可变对象,而老的对象不会改变。
二、react中如何减少render函数渲染的次数
在react中当this.state、this.props发生改变的时候render函数就会执行,但有些时候this.state、this.props没有发生改变的时候render函数也会执行。那么如何减少render函数的执行次数,可以封装一个BaseComponent来减少react中render函数的执行次数。
import React,{Component}from 'react'; import {is} from 'immutable'; class BaseComponent extends Component { shouldComponentUpdate(newProps, newState) { const thisProps = this.props || {}; const thisState = this.state || {}; newState = newState || {}; newProps = newProps || {}; if (Object.keys(thisProps).length !== Object.keys(newProps).length || Object.keys(thisState).length !== Object.keys(newState).length) { return true; } for (const key in newProps) { if (!is(thisProps[key], newProps[key])) { return true; } } for (const key in newState) { if (!is(thisState[key], newState[key])) { return true; } } return false; } } export default BaseComponent;
继承BaseComponent来创建开发中所需要的组件:
import BaseComponent from "@common/BaseComponent"; export default class Home extends BaseComponent{ constructor(){ super() } ........ }
35、react中key值的作用
react中的key属性,它是一个特殊的属性,它的出现不是给开发者使用的,而是给react自己使用,有了key属性后,就可以与组件建立一种对应关系。react利用key来识别组件,它是一种身份标识,就像每个人都有身份证一样,每个key对应一个组件,相同的key react认为是同一个组件,这样后续相同的key对应的组件都不会被创建。
key值相同:如果两个元素的key相同,且满足第一点元素类型相同,若元素属性有所变化,则react只更新组件对应的属性,这种情况下性能开销会相对较小。
key值不相同:在render函数执行的时候,新旧两个虚拟DOM会进行对比,如果两个元素有不同的key,那么前后两次渲染中就会被认为是不同的元素,这时候旧的那个元素会被销毁,新的元素会被创建。
//更新前 render(){ return( <List key="1"/> ) } //更新后 render(){ return( <List key="2"/> ) }
例子:
//tree1 <ul> <li key="1">1</li> <li key="2">2</li> </ul> //tree2 <ul> <li key="1">1</li> <li key="3">3</li> <li key="2">2</li> </ul>
如果没有key值,react并不会执行插入操作,它会直接移除原先的第二个子元素,然后在append进去剩下的子元素,而其实我们这个操作只需要一个insert操作就能完成。为了解决这种问题,react需要我们提供一个key来帮助更新,减少性能开销。
如果有key值,react就会通过key来发现剩下tree2的第二个子元素不是原先tree1的第二个元素,原先的第二个元素被挪到下面去了,因此在操作的时候就会直接指向insert操作,来减少DOM操作的性能开销。
不推荐用属性中的index来做key值
大部分情况下我们要在执行数据遍历的时候会用index来表示元素的key,这样做其实并不是很合理。我们用key的真实目的是为了标识前后两次渲染中元素的对应关系,防止发生不必要的更新操作。那么如果我们用index来标识key,数据在执行插入、排序等操作之后,原先的index并不再对应到原先的值,那么这个key就失去了本身的意义,还会带来其他问题。
注意事项
react中的key值必须保证其唯一和稳定性
下面案例中的key值以Math.random()随机生成而定,这使得数组元素中的每项都重新销毁然后重新创建,有一定的性能开销
{ dataList.map((item,index)=>{ return <div key={Math.random()}>{item.name}</div> }) }