2021高频前端面试题汇总之React篇

React视频教程系列

React 实战:CNode视频教程

残缺教程目录:点击查看

React经典教程-从入门到精通

残缺教程目录:点击查看

最新最全前端毕设我的项目(小程序+VUE+Noed+React+uni app+Express+Mongodb)

残缺教程目录:点击查看

2021前端React精品教程

残缺教程目录:点击查看

1. React的事件和一般的HTML事件有什么不同?

区别:

  • 对于事件名称命名形式,原生事件为全小写,react 事件采纳小驼峰;
  • 对于事件函数解决语法,原生事件为字符串,react 事件为函数;
  • react 事件不能采纳 return false 的形式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。

合成事件是 react 模仿原生 DOM 事件所有能力的一个事件对象,其长处如下:

  • 兼容所有浏览器,更好的跨平台;
  • 将事件对立寄存在一个数组,防止频繁的新增与删除(垃圾回收)。
  • 不便 react 对立治理和事务机制。

事件的执行程序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为须要冒泡到document 上合成事件才会执行。

2. React 组件中怎么做事件代理?它的原理是什么?

React基于Virtual DOM实现了一个SyntheticEvent层(合成事件层),定义的事件处理器会接管到一个合成事件对象的实例,它合乎W3C规范,且与原生的浏览器事件领有同样的接口,反对冒泡机制,所有的事件都主动绑定在最外层上。

在React底层,次要对合成事件做了两件事:

  • 事件委派: React会把所有的事件绑定到构造的最外层,应用对立的事件监听器,这个事件监听器上维持了一个映射来保留所有组件外部事件监听和处理函数。
  • 主动绑定: React组件中,每个办法的上下文都会指向该组件的实例,即主动绑定this为以后组件。

3. 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的一些毛病。

4. 对React-Fiber的了解,它解决了什么问题?

React V15 在渲染时,会递归比对 VirtualDOM 树,找出须要变动的节点,而后同步更新它们, 零打碎敲。这个过程期间, React 会占据浏览器资源,这会导致用户触发的事件得不到响应,并且会导致掉帧,导致用户感觉到卡顿

为了给用户制作一种利用很快的“假象”,不能让一个工作长期霸占着资源。 能够将浏览器的渲染、布局、绘制、资源加载(例如 HTML 解析)、事件响应、脚本执行视作操作系统的“过程”,须要通过某些调度策略正当地调配 CPU 资源,从而进步浏览器的用户响应速率, 同时兼顾工作执行效率。

所以 React 通过Fiber 架构,让这个执行过程变成可被中断。“适时”地让出 CPU 执行权,除了能够让浏览器及时地响应用户的交互,还有其余益处:

  • 分批延时对DOM进行操作,防止一次性操作大量 DOM 节点,能够失去更好的用户体验;
  • 给浏览器一点喘息的机会,它会对代码进行编译优化(JIT)及进行热代码优化,或者对 reflow 进行修改。

核心思想: Fiber 也称协程或者纤程。它和线程并不一样,协程自身是没有并发或者并行能力的(须要配合线程),它只是一种管制流程的让出机制。让出 CPU 的执行权,让 CPU 能在这段时间执行其余的操作。渲染的过程能够被中断,能够将控制权交回浏览器,让位给高优先级的工作,浏览器闲暇后再复原渲染。

5. Component, Element, Instance 之间有什么区别和分割?

  • 元素: 一个元素element是一个一般对象(plain object),形容了对于一个DOM节点或者其余组件component,你想让它在屏幕上出现成什么样子。元素element能够在它的属性props中蕴含其余元素(译注:用于造成元素树)。创立一个React元素element老本很低。元素element创立之后是不可变的。
  • 组件: 一个组件component能够通过多种形式申明。能够是带有一个render()办法的类,简略点也能够定义为一个函数。这两种状况下,它都把属性props作为输出,把返回的一棵元素树作为输入。
  • 实例: 一个实例instance是你在所写的组件类component class中应用关键字this所指向的货色(译注:组件实例)。它用来存储本地状态和响应生命周期事件很有用。

函数式组件(Functional component)基本没有实例instance。类组件(Class component)有实例instance,然而永远也不须要间接创立一个组件的实例,因为React帮咱们做了这些。

6. React.createClass和extends Component的区别有哪些?

React.createClass和extends Component的bai区别次要在于:

(1)语法区别

  • createClass实质上是一个工厂函数,extends的形式更加靠近最新的ES6标准的class写法。两种形式在语法上的差异次要体现在办法的定义和动态属性的申明上。
  • createClass形式的办法定义应用逗号,隔开,因为creatClass实质上是一个函数,传递给它的是一个Object;而class的形式定义方法时务必谨记不要应用逗号隔开,这是ES6 class的语法标准。

(2)propType 和 getDefaultProps

  • React.createClass:通过proTypes对象和getDefaultProps()办法来设置和获取props.
  • React.Component:通过设置两个属性propTypes和defaultProps

(3)状态的区别

  • React.createClass:通过getInitialState()办法返回一个蕴含初始值的对象
  • React.Component:通过constructor设置初始状态

(4)this区别

  • React.createClass:会正确绑定this
  • React.Component:因为应用了 ES6,这里会有些微不同,属性并不会主动绑定到 React 类的实例上。

(5)Mixins

  • React.createClass:应用 React.createClass 的话,能够在创立组件时增加一个叫做 mixins 的属性,并将可供混合的类的汇合以数组的模式赋给 mixins。
  • 如果应用 ES6 的形式来创立组件,那么 React mixins 的个性将不能被应用了。

7. 对React的插槽(Portals)的了解,如何应用,有哪些应用场景

React 官网对 Portals 的定义:

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优良的计划

Portals 是React 16提供的官网解决方案,使得组件能够脱离父组件层级挂载在DOM树的任何地位。艰深来讲,就是咱们 render 一个组件,但这个组件的 DOM 构造并不在本组件内。

Portals语法如下:

ReactDOM.createPortal(child, container);复制代码
  • 第一个参数 child 是可渲染的 React 子项,比方元素,字符串或者片段等;
  • 第二个参数 container 是一个 DOM 元素。

个别状况下,组件的render函数返回的元素会被挂载在它的父级组件上:

import DemoComponent from './DemoComponent';render() {  // DemoComponent元素会被挂载在id为parent的div的元素上  return (    <div id="parent">        <DemoComponent />    </div>  );}

8. Redux 原理及工作流程

(1)原理 Redux源码次要分为以下几个模块文件

  • compose.js 提供从右到左进行函数式编程
  • createStore.js 提供作为生成惟一store的函数
  • combineReducers.js 提供合并多个reducer的函数,保障store的唯一性
  • bindActionCreators.js 能够让开发者在不间接接触dispacth的前提下进行更改state的操作
  • applyMiddleware.js 这个办法通过中间件来加强dispatch的性能
const actionTypes = {    ADD: 'ADD',    CHANGEINFO: 'CHANGEINFO',}const initState = {    info: '初始化',}export default function initReducer(state=initState, action) {    switch(action.type) {        case actionTypes.CHANGEINFO:            return {                ...state,                info: action.preload.info || '',            }        default:            return { ...state };    }}export default function createStore(reducer, initialState, middleFunc) {    if (initialState && typeof initialState === 'function') {        middleFunc = initialState;        initialState = undefined;    }    let currentState = initialState;    const listeners = [];    if (middleFunc && typeof middleFunc === 'function') {        // 封装dispatch         return middleFunc(createStore)(reducer, initialState);    }    const getState = () => {        return currentState;    }    const dispatch = (action) => {        currentState = reducer(currentState, action);        listeners.forEach(listener => {            listener();        })    }    const subscribe = (listener) => {        listeners.push(listener);    }    return {        getState,        dispatch,        subscribe    }}复制代码

(2)工作流程

  • const store= createStore(fn)生成数据;
  • action: {type: Symble('action01), payload:'payload' }定义行为;
  • dispatch发动action:store.dispatch(doSomething('action001'));
  • reducer:解决action,返回新的state;

艰深点解释:

  • 首先,用户(通过View)收回Action,收回形式就用到了dispatch办法
  • 而后,Store主动调用Reducer,并且传入两个参数:以后State和收到的Action,Reducer会返回新的State
  • State—旦有变动,Store就会调用监听函数,来更新View

以 store 为外围,能够把它看成数据存储核心,然而他要更改数据的时候不能间接批改,数据批改更新的角色由Reducers来负责,store只做存储,中间人,当Reducers的更新实现当前会通过store的订阅来告诉react component,组件把新的状态从新获取渲染,组件中也能被动发送action,创立action后这个动作是不会执行的,所以要dispatch这个action,让store通过reducers去做更新React Component 就是react的每个组件。

9. 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)}

10. Redux 状态管理器和变量挂载到 window 中有什么区别

两者都是存储数据以供前期应用。然而Redux状态更改可回溯——Time travel,数据多了的时候能够很清晰的晓得改变在哪里产生,残缺的提供了一套状态管理模式。

随着 JavaScript 单页利用开发日趋简单,JavaScript 须要治理比任何时候都要多的 state (状态)。 这些 state 可能包含服务器响应、缓存数据、本地生成尚未长久化到服务器的数据,也包含 UI状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

治理一直变动的 state 十分艰难。如果一个 model 的变动会引起另一个 model 变动,那么当 view 变动时,就可能引起对应 model 以及另一个model 的变动,顺次地,可能会引起另一个 view 的变动。直至你搞不清楚到底产生了什么。state 在什么时候,因为什么起因,如何变动未然不受管制。 当零碎变得盘根错节的时候,想重现问题或者增加新性能就会变得举步维艰。 如果这还不够蹩脚,思考一些来自前端开发畛域的新需要,如更新调优、服务端渲染、路由跳转前申请数据等等。前端开发者正在禁受前所未有的复杂性,难道就这么放弃了吗?当然不是。

这里的复杂性很大水平上来自于:咱们总是将两个难以理清的概念混同在一起:变动和异步。 能够称它们为曼妥思和可乐。如果把二者离开,能做的很好,但混到一起,就变得一团糟。一些库如 React 视图在视图层禁止异步和间接操作 DOM来解决这个问题。美中不足的是,React 仍旧把解决 state 中数据的问题留给了你。Redux就是为了帮你解决这个问题。