2021高频前端面试题汇总之React篇
React视频教程系列
React 实战:CNode视频教程
残缺教程目录:点击查看
1. React 事件机制
<div onClick={this.handleClick.bind(this)}>点我</div>复制代码
React并不是将click事件绑定到了div的实在DOM上,而是在document处监听了所有的事件,当事件产生并且冒泡到document处的时候,React将事件内容封装并交由真正的处理函数运行。这样的形式不仅仅缩小了内存的耗费,还能在组件挂在销毁时对立订阅和移除事件。
除此之外,冒泡到document上的事件也不是原生的浏览器事件,而是由react本人实现的合成事件(SyntheticEvent)。因而如果不想要是事件冒泡的话应该调用event.preventDefault()办法,而不是调用event.stopProppagation()办法。 JSX 上写的事件并没有绑定在对应的实在 DOM 上,而是通过事件代理的形式,将所有的事件都对立绑定在了 document
上。这样的形式不仅缩小了内存耗费,还能在组件挂载销毁时对立订阅和移除事件。
另外冒泡到 document
上的事件也不是原生浏览器事件,而是 React 本人实现的合成事件(SyntheticEvent)。因而咱们如果不想要事件冒泡的话,调用 event.stopPropagation
是有效的,而应该调用 event.preventDefault
。
实现合成事件的目标如下:
- 合成事件首先抹平了浏览器之间的兼容问题,另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力;
- 对于原生浏览器事件来说,浏览器会给监听器创立一个事件对象。如果你有很多的事件监听,那么就须要调配很多的事件对象,造成高额的内存调配问题。然而对于合成事件来说,有一个事件池专门来治理它们的创立和销毁,当事件须要被应用时,就会从池子中复用对象,事件回调完结后,就会销毁事件对象上的属性,从而便于下次复用事件对象。
2. React 高阶组件、Render props、hooks 有什么区别,为什么要一直迭代
这三者是目前react解决代码复用的次要形式:
- 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
- render props是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术,更具体的说,render prop 是一个用于告知组件须要渲染什么内容的函数 prop。
- 通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个应用场景更加简略。这两种模式仍有用武之地,(例如,一个虚构滚动条组件或者会有一个 renderltem 属性,或是一个可见的容器组件或者会有它本人的 DOM 构造)。但在大部分场景下,Hook 足够了,并且可能帮忙缩小嵌套。
(1)HOC 官网解释∶
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。
简言之,HOC是一种组件的设计模式,HOC承受一个组件和额定的参数(如果须要),返回一个新的组件。HOC 是纯函数,没有副作用。
// hoc的定义function withSubscription(WrappedComponent, selectData) { return class extends React.Component { constructor(props) { super(props); this.state = { data: selectData(DataSource, props) }; } // 一些通用的逻辑解决 render() { // ... 并应用新数据渲染被包装的组件! return <WrappedComponent data={this.state.data} {...this.props} />; } };// 应用const BlogPostWithSubscription = withSubscription(BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id));复制代码
HOC的优缺点∶
- 长处∶ 逻辑服用、不影响被包裹组件的外部逻辑。
- 毛病∶ hoc传递给被包裹组件的props容易和被包裹后的组件重名,进而被笼罩
(2)Render props 官网解释∶
"render prop"是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术
具备render prop 的组件承受一个返回React元素的函数,将render的渲染逻辑注入到组件外部。在这里,"render"的命名能够是任何其余无效的标识符。
// DataProvider组件外部的渲染逻辑如下class DataProvider extends React.Components { state = { name: 'Tom' } render() { return ( <div> <p>共享数据组件本人外部的渲染逻辑</p> { this.props.render(this.state) } </div> ); }}// 调用形式<DataProvider render={data => ( <h1>Hello {data.name}</h1>)}/>复制代码
由此能够看到,render props的优缺点也很显著∶
- 长处:数据共享、代码复用,将组件内的state作为props传递给调用者,将渲染逻辑交给调用者。
- 毛病:无奈在 return 语句外拜访数据、嵌套写法不够优雅
(3)Hooks 官网解释∶
Hook是 React 16.8 的新增个性。它能够让你在不编写 class 的状况下应用 state 以及其余的 React 个性。通过自定义hook,能够复用代码逻辑。
// 自定义一个获取订阅数据的hookfunction useSubscription() { const data = DataSource.getComments(); return [data];}// function CommentList(props) { const {data} = props; const [subData] = useSubscription(); ...}// 应用<CommentList data='hello' />复制代码
以上能够看出,hook解决了hoc的prop笼罩的问题,同时应用的形式解决了render props的嵌套天堂的问题。hook的长处如下∶
- 应用直观;
- 解决hoc的prop 重名问题;
- 解决render props 因共享数据 而呈现嵌套天堂的问题;
- 能在return之外应用数据的问题。
须要留神的是:hook只能在组件顶层应用,不可在分支语句中应用。
总结∶ Hoc、render props和hook都是为了解决代码复用的问题,然而hoc和render props都有特定的应用场景和显著的毛病。hook是react16.8更新的新的API,让组件逻辑复用更简洁明了,同时也解决了hoc和render props的一些毛病。
3. React.Component 和 React.PureComponent 的区别
PureComponent示意一个纯组件,能够用来优化React程序,缩小render函数执行的次数,从而进步组件的性能。
在React中,当prop或者state发生变化时,能够通过在shouldComponentUpdate生命周期函数中执行return false来阻止页面的更新,从而缩小不必要的render执行。React.PureComponent会主动执行 shouldComponentUpdate。
不过,pureComponent中的 shouldComponentUpdate() 进行的是浅比拟,也就是说如果是援用数据类型的数据,只会比拟不是同一个地址,而不会比拟这个地址外面的数据是否统一。浅比拟会疏忽属性和或状态渐变状况,其实也就是数据援用指针没有变动,而数据产生扭转的时候render是不会执行的。如果须要从新渲染那么就须要从新开拓空间援用数据。PureComponent个别会用在一些纯展现组件上。
应用pureComponent的益处:当组件更新时,如果组件的props或者state都没有扭转,render函数就不会触发。省去虚构DOM的生成和比照过程,达到晋升性能的目标。这是因为react主动做了一层浅比拟。
4. Redux 中异步的申请怎么解决
能够在 componentDidmount 中间接进⾏申请⽆须借助redux。然而在⼀定规模的项⽬中,上述⽅法很难进⾏异步流的治理,通常状况下咱们会借助redux的异步中间件进⾏异步解决。redux异步流中间件其实有很多,当下支流的异步中间件有两种redux-thunk、redux-saga。
(1)应用react-thunk中间件
redux-thunk长处:
- 体积⼩: redux-thunk的实现⽅式很简略,只有不到20⾏代码
- 使⽤简略: redux-thunk没有引⼊像redux-saga或者redux-observable额定的范式,上⼿简略
redux-thunk缺点:
- 样板代码过多: 与redux自身⼀样,通常⼀个申请须要⼤量的代码,⽽且很多都是反复性质的
- 耦合重大: 异步操作与redux的action偶合在⼀起,不⽅便治理
- 性能孱弱: 有⼀些理论开发中常⽤的性能须要⾃⼰进⾏封装
应用步骤:
- 配置中间件,在store的创立中配置
import {createStore, applyMiddleware, compose} from 'redux';import reducer from './reducer';import thunk from 'redux-thunk'// 设置调试工具const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;// 设置中间件const enhancer = composeEnhancers( applyMiddleware(thunk));const store = createStore(reducer, enhancer);export default store;复制代码
- 增加一个返回函数的actionCreator,将异步申请逻辑放在外面
/** 发送get申请,并生成相应action,更新store的函数 @param url {string} 申请地址 @param func {function} 真正须要生成的action对应的actionCreator @return {function} */// dispatch为主动接管的store.dispatch函数 export const getHttpAction = (url, func) => (dispatch) => { axios.get(url).then(function(res){ const action = func(res.data) dispatch(action) })}复制代码
- 生成action,并发送action
componentDidMount(){ var action = getHttpAction('/getData', getInitTodoItemAction) // 发送函数类型的action时,该action的函数领会主动执行 store.dispatch(action)}复制代码
(2)应用redux-saga中间件
redux-saga长处:
- 异步解耦: 异步操作被被转移到独自 saga.js 中,不再是掺杂在 action.js 或 component.js 中
- action解脱thunk function: dispatch 的参数仍然是⼀个纯正的 action (FSA),⽽不是充斥 “⿊魔法” thunk function
- 异样解决: 受害于 generator function 的 saga 实现,代码异样/申请失败 都能够间接通过 try/catch 语法间接捕捉解决
- 性能强⼤: redux-saga提供了⼤量的Saga 辅助函数和Effect 创立器供开发者使⽤,开发者⽆须封装或者简略封装即可使⽤
- 灵便: redux-saga能够将多个Saga能够串⾏/并⾏组合起来,造成⼀个⾮常实⽤的异步flow
- 易测试,提供了各种case的测试⽅案,包含mock task,分⽀笼罩等等
redux-saga缺点:
- 额定的学习老本: redux-saga不仅在使⽤难以了解的 generator function,⽽且无数⼗个API,学习老本远超redux-thunk,最重要的是你的额定学习老本是只服务于这个库的,与redux-observable不同,redux-observable尽管也有额定学习老本然而背地是rxjs和⼀整套思维
- 体积庞⼤: 体积略⼤,代码近2000⾏,min版25KB左右
- 性能过剩: 实际上并发管制等性能很难⽤到,然而咱们仍然须要引⼊这些代码
- ts⽀持不敌对: yield⽆法返回TS类型
redux-saga能够捕捉action,而后执行一个函数,那么能够把异步代码放在这个函数中,应用步骤如下:
- 配置中间件
import {createStore, applyMiddleware, compose} from 'redux';import reducer from './reducer';import createSagaMiddleware from 'redux-saga'import TodoListSaga from './sagas'const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;const sagaMiddleware = createSagaMiddleware()const enhancer = composeEnhancers( applyMiddleware(sagaMiddleware));const store = createStore(reducer, enhancer);sagaMiddleware.run(TodoListSaga)export default store;复制代码
- 将异步申请放在sagas.js中
import {takeEvery, put} from 'redux-saga/effects'import {initTodoList} from './actionCreator'import {GET_INIT_ITEM} from './actionTypes'import axios from 'axios'function* func(){ try{ // 能够获取异步返回数据 const res = yield axios.get('/getData') const action = initTodoList(res.data) // 将action发送到reducer yield put(action) }catch(e){ console.log('网络申请失败') }}function* mySaga(){ // 主动捕捉GET_INIT_ITEM类型的action,并执行func yield takeEvery(GET_INIT_ITEM, func)}export default mySaga复制代码
- 发送action
componentDidMount(){ const action = getInitTodoItemAction() store.dispatch(action)}
5. Redux 中间件是什么?承受几个参数?柯里化函数两端的参数具体是什么?
Redux 的中间件提供的是位于 action 被发动之后,达到 reducer 之前的扩大点,换而言之,本来 view -→> action -> reducer -> store 的数据流加上中间件后变成了 view -> action -> middleware -> reducer -> store ,在这一环节能够做一些"副作用"的操作,如异步申请、打印日志等。
applyMiddleware源码:
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { // 利用传入的createStore和reducer和创立一个store const store = createStore(...args) let dispatch = () => { throw new Error() } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 让每个 middleware 带着 middlewareAPI 这个参数别离执行一遍 const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } }}复制代码
从applyMiddleware中能够看出∶
- redux中间件承受一个对象作为参数,对象的参数上有两个字段 dispatch 和 getState,别离代表着 Redux Store 上的两个同名函数。
- 柯里化函数两端一个是 middewares,一个是store.dispatch
6. 对 React Hook 的了解,它的实现原理是什么
React-Hooks 是 React 团队在 React 组件开发实际中,逐步认知到的一个改良点,这背地其实波及对类组件和函数组件两种组件模式的思考和偏重。
(1)类组件: 所谓类组件,就是基于 ES6 Class 这种写法,通过继承 React.Component 得来的 React 组件。以下是一个类组件:
class DemoClass extends React.Component { state = { text: "" }; componentDidMount() { //... } changeText = (newText) => { this.setState({ text: newText }); }; render() { return ( <div className="demoClass"> <p>{this.state.text}</p> <button onClick={this.changeText}>批改</button> </div> ); }}复制代码
能够看出,React 类组件外部预置了相当多的“现成的货色”等着咱们去调度/定制,state 和生命周期就是这些“现成货色”中的典型。要想得到这些货色,难度也不大,只须要继承一个 React.Component 即可。
当然,这也是类组件的一个不便,它太繁冗了,对于解决许多问题来说,编写一个类组件切实是一个过于简单的姿态。简单的姿态必然带来昂扬的了解老本,这也是咱们所不想看到的。除此之外,因为开发者编写的逻辑在封装后是和组件粘在一起的,这就使得类组件外部的逻辑难以实现拆分和复用。
(2)函数组件:函数组件就是以函数的状态存在的 React 组件。晚期并没有 React-Hooks,函数组件外部无奈定义和保护 state,因而它还有一个别名叫“无状态组件”。以下是一个函数组件:
function DemoFunction(props) { const { text } = props return ( <div className="demoFunction"> <p>{`函数组件接管的内容:[${text}]`}</p> </div> );}复制代码
相比于类组件,函数组件肉眼可见的特质天然包含轻量、灵便、易于组织和保护、较低的学习老本等。
通过比照,从状态上能够对两种组件做辨别,它们之间的区别如下:
- 类组件须要继承 class,函数组件不须要;
- 类组件能够拜访生命周期办法,函数组件不能;
- 类组件中能够获取到实例化后的 this,并基于这个 this 做各种各样的事件,而函数组件不能够;
- 类组件中能够定义并保护 state(状态),而函数组件不能够;
除此之外,还有一些其余的不同。通过下面的区别,咱们不能说谁好谁坏,它们各有本人的劣势。在 React-Hooks 呈现之前,类组件的能力边界显著强于函数组件。
实际上,类组件和函数组件之间,是面向对象和函数式编程这两套不同的设计思维之间的差别。而函数组件更加符合 React 框架的设计理念: React 组件自身的定位就是函数,一个输出数据、输入 UI 的函数。作为开发者,咱们编写的是申明式的代码,而 React 框架的次要工作,就是及时地把申明式的代码转换为命令式的 DOM 操作,把数据层面的形容映射到用户可见的 UI 变动中去。这就意味着从原则上来讲,React 的数据应该总是紧紧地和渲染绑定在一起的,而类组件做不到这一点。函数组件就真正地将数据和渲染绑定到了一起。函数组件是一个更加匹配其设计理念、也更有利于逻辑拆分与重用的组件表达形式。
为了能让开发者更好的的去编写函数式组件。于是,React-Hooks 便应运而生。
React-Hooks 是一套可能使函数组件更弱小、更灵便的“钩子”。
函数组件比起类组件少了很多货色,比方生命周期、对 state 的治理等。这就给函数组件的应用带来了十分多的局限性,导致咱们并不能应用函数这种模式,写出一个真正的全功能的组件。而React-Hooks 的呈现,就是为了帮忙函数组件补齐这些(绝对于类组件来说)缺失的能力。
如果说函数组件是一台笨重的快艇,那么 React-Hooks 就是一个内容丰盛的零部件箱。“重装战舰”所预置的那些设施,这个箱子里根本全都有,同时它还不强制你全都要,而是容许你自在地抉择和应用你须要的那些能力,而后将这些能力以 Hook(钩子)的模式“钩”进你的组件里,从而定制出一个最适宜你的“专属战舰”。