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

29次阅读

共计 10486 个字符,预计需要花费 27 分钟才能阅读完成。

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,能够复用代码逻辑。

// 自定义一个获取订阅数据的 hook
function 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 就是为了帮你解决这个问题。

正文完
 0