react面试题

1. 当你调用 setState 的时候,发生了什么事?

参考答案:将传递给 setState 的对象合并到组件的当前状态,这将启动一个和解的过程,构建一个新的 react 元素树,与上一个元素树进行对比( diff ),从而进行最小化的重渲染。

参与互动

2. React 项目用过什么脚手架(本题是开放性题目)

参考答案:create-react-app 是最常用 的脚手架,一定要说出来!

  • Create React App:如果你是在学习 React 或创建一个新的单页应用

Create React App是FaceBook的React团队官方出的一个构建React单页面应用的脚手架工具。它本身集成了Webpack,并配置了一系列内置的loader和默认的npm的脚本,可以很轻松的实现零配置就可以快速开发React的应用。

  • Next. js:如果你是在用 Node. js 构建服务端渲染的网站

Next. js 为您提供生产环境所需的所有功能以及最佳的开发体验:包括静态及服务器端融合渲染、 支持 TypeScript、智能化打包、 路由预取等功能 无需任何配置。

  • Gatsby:如果你是在构建面向内容的静态网站

Gatsby. js 是基于 React 构建的、速度非常快的、现代化网站生成器。超越静态网站: 用 Gatsby 可以构建博客、电子商务网站、成熟的应用程序等。

  • nwb:用于React应用程序、库和其他web npm模块的工具包

  • razzle:创建没有配置的服务器呈现的通用JavaScript应用程序

Razzle是类似于next. js的简单服务端框架, 用于在服务端渲染 React 应用程序。

  • Neutrino:创建和构建零初始配置的现代JavaScript应用程序

  • Yeoman:

Yeoman提供generator系统,一个generator是一个插件,在我们在一个完整的项目上使用‘yo’命令时,会运行该generator。通过这些官方的Generators,推出了Yeoman工作流,工作流是一个健壮、有自己特色的客户端堆栈,包含能快速构建漂亮的网络应用的工具和框架。Yeoman提供了负责开始项目开发的一切,没有任何让人头痛的手动配置。

采用模块化结构,Yeoman利用从几个开源社区网站学习到的成功和教训,以确保栈开发人员越来越智能的进行开发。基于良好的文档基础以及深思熟虑的项目构建过程,Yeoman提供测试和其他更多技术 ,因此开发人员可以更专注于解决方案而不用去担心其他小事。

Yeoman主要提供了三个工具:脚手架(yo),构建工具(grunt),包管理器(bower)。这三个工具是分别独立开发的,但是需要配合使用,来实现我们更高效的工作流模式。

  • umi. js:

umi,中文可发音为乌米,是一个可插拔的企业级 react 应用框架。你可以将它简单的理解为一个专注性能的类 next. js 前端框架,并通过约定、自动生成和解析代码等方式来辅助开发,减少我们开发者的代码量。

  • react-cli脚手架

  • Rekit脚手架

参与互动

3. 功能组件( Functional Component )与类组件( Class Component )如何选择?

参考答案:如果您的组件具有状态( state ) 或 生命周期方法,请使用 Class 组件。否则,使用功能组件

解析:

React中有两种组件:函数组件(Functional Components) 和类组件(Class Components)。据我观察,大部分同学都习惯于用类组件,而很少会主动写函数组件,包括我自己也是这样。但实际上,在使用场景和功能实现上,这两类组件是有很大区别的。

来看一个函数组件的例子:

function Welcome = (props) => {
  const sayHi = () => {
    alert( `Hi ${props.name}` );
  }
  return (
    
   

Hello, {props.name}

     
) }

把上面的函数组件改写成类组件:

import React from 'react'
​
class Welcome extends React.Component {
  constructor(props) {
    super(props);
    this.sayHi = this.sayHi.bind(this);
  }
  sayHi() {
    alert( `Hi ${this.props.name}` );
  }
  render() {
    return (
      
     

Hello, {this.props.name}

         
  ) } }

下面让我们来分析一下两种实现的区别:

  1. 第一眼直观的区别是,函数组件的代码量比类组件要少一些,所以函数组件比类组件更加简洁。千万不要小看这一点,对于我们追求极致的程序员来说,这依然是不可忽视的。

  2. 函数组件看似只是一个返回值是DOM结构的函数,其实它的背后是无状态组件(Stateless Components)的思想。函数组件中,你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展示性组件(Presentational Components),接收Props,渲染DOM,而不关注其他逻辑。

  3. 函数组件中没有this。所以你再也不需要考虑this带来的烦恼。而在类组件中,你依然要记得绑定this这个琐碎的事情。如示例中的sayHi。

  4. 函数组件更容易理解。当你看到一个函数组件时,你就知道它的功能只是接收属性,渲染页面,它不执行与UI无关的逻辑处理,它只是一个纯函数。而不用在意它返回的DOM结构有多复杂。

  5. 性能。目前React还是会把函数组件在内部转换成类组件,所以使用函数组件和使用类组件在性能上并无大的差异。但是,React官方已承诺,未来将会优化函数组件的性能,因为函数组件不需要考虑组件状态和组件生命周期方法中的各种比较校验,所以有很大的性能提升空间。

  6. 函数组件迫使你思考最佳实践。这是最重要的一点。组件的主要职责是UI渲染,理想情况下,所有的组件都是展示性组件,每个页面都是由这些展示性组件组合而成。如果一个组件是函数组件,那么它当然满足这个要求。所以牢记函数组件的概念,可以让你在写组件时,先思考这个组件应不应该是展示性组件。更多的展示性组件意味着更多的组件有更简洁的结构,更多的组件能被更好的复用。

所以,当你下次在动手写组件时,一定不要忽略了函数组件,应该尽可能多地使用函数组件。

参与互动

4. React 中 keys 的作用是什么?

参考答案:Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。

render () {
  return (
    
    {this.state.todoItems.map(({item, key}) => { return
  • {item}
  • })}
) }

在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。

参与互动

5. React 优势

参考答案:

1、React 速度很快:它并不直接对 DOM 进行操作,引入了一个叫做虚拟 DOM 的概念,安插在 javascript 逻辑和实际的 DOM 之间,性能好。

2、跨浏览器兼容:虚拟 DOM 帮助我们解决了跨浏览器问题,它为我们提供了标准化的 API,甚至在 IE8 中都是没问题的。

3、一切都是 component:代码更加模块化,重用代码更容易,可维护性高。

4、单向数据流:Flux 是一个用于在 JavaScript 应用中创建单向数据层的架构,它随着 React 视图库的开发而被 Facebook 概念化。

5、同构、纯粹的 javascript:因为搜索引擎的爬虫程序依赖的是服务端响应而不是 JavaScript 的执行,预渲染你的应用有助于搜索引擎优化。

6、兼容性好:比如使用 RequireJS 来加载和打包,而 Browserify 和 Webpack 适用于构建大型应用。它们使得那些艰难的任务不再让人望而生畏。

参与互动

6. React 很多个 setState 为什么是执行完再 render

参考答案:react为了提高整体的渲染性能,会将一次渲染周期中的state进行合并,在这个渲染周期中对所有setState的所有调用都会被合并起来之后,再一次性的渲染,这样可以避免频繁的调用setState导致频繁的操作dom,提高渲染性能。

具体的实现方面,可以简单的理解为react中存在一个状态变量isBatchingUpdates,当处于渲染周期开始时,这个变量会被设置成true,渲染周期结束时,会被设置成false,react会根据这个状态变量,当出在渲染周期中时,仅仅只是将当前的改变缓存起来,等到渲染周期结束时,再一次性的全部render。

参与互动

7. react diff 原理(常考,大厂必考)

参考答案:

  1. 把树形结构按照层级分解,只比较同级元素。

  2. 给列表结构的每个单元添加唯一的 key 属性,方便比较。

  3. React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)

  4. 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty. 到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.

  5. 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

参与互动

8. react 生命周期函数

参考答案:

react15生命周期

  1. 初始化阶段:

getDefaultProps: 获取实例的默认属性 getInitialState: 获取实例的初始化状态 componentWillMount:组件即将被装载、渲染到页面上 render: 组件在这里生成虚拟的 DOM 节点 componentDidMount: 组件真正在被装载之后

  1. 运行中阶段:

componentWillReceiveProps: 组件将要接收到属性的时候调用 shouldComponentUpdate: 组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了) componentWillUpdate: 组件即将更新不能修改属性和状态 render: 组件重新描绘 componentDidUpdate: 组件已经更新

  1. 销毁阶段:

componentWillUnmount: 组件即将销毁

react16生命周期

React 在v16. 3版本中将 componentWillMount, componentWillReceiveProps 以及componentWillUpdate 加上了UNSAFE_前缀,这些钩子将在React 17. 0废除

新引入的两个生命周期函数

  • getDerivedStateFromProps: 是一个静态方法, 是一个和组件自身"不相关"的角色. 在这个静态方法中, 除了两个默认的位置参数 nextProps 和 currentState 以外, 你无法访问任何组件上的数据.

  • getSnapshotBeforeUpdate: 获取render之前的dom状态

参与互动

9. shouldComponentUpdate 是做什么的?(react 性能优化是哪个周期函数?)

参考答案:

  1. shouldComponentUpdate询问组件是否需要更新的一个钩子函数,判断数据是否需要重新渲染,返回一个布尔值。默认的返回值是true,需要重新render()。若如果返回值是false则不触发渲染,利用这个生命周期函数可以强制关闭不需要更新的子组件来提升渲染性能。

  2. 这个方法用来判断是否需要调用 render 方法重新描绘 dom。

  3. 因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。

参与互动

10. 为什么虚拟 dom 会提高性能?(必考)

参考答案:

虚拟dom(virtual dom) 其实就是一个JavaScript对象,通过这个JavaScript对象来描述真实dom。

真实dom:以前没有虚拟dom,如果需要比较两个页面的差异,我们需要通过对真实dom进行比对。真实dom节点是非常复杂的,它里面会绑定的事件,它会有属性,背后会有各种方法,会频繁触发重排与重绘,所以两个真实dom比对,非常耗性能。

总损耗 = 真实DOM完全增删改 + (可能较多的节点)重排与重绘

虚拟dom:相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提髙性能。

总损耗 = 虚拟DOM增删改 + (与Diff算法效率有关)真实DOM差异增删改 + (较少的节点)重排与重绘

具体实现步骤如下:

  1. 用JavaScript对象结构表示DOM树的结构;然后用这个树构建一个真正的DOM树,插到文档当中;

  2. 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;

  3. 把步骤2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

参与互动

11. React 中 refs 的作用是什么?

参考答案:

refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。我们可以为元素添加 ref 属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回:

class CustomForm extends Component {
  handleSubmit = () => {
    console.log("Input Value: ", this.input.value)
  }
  render () {
    return (
      
this.input = input} />
) } }

上述代码中的 input 域包含了一个 ref 属性,该属性声明的回调函数会接收 input 对应的 DOM 元素,我们将其绑定到 this 指针以便在其他的类函数中使用。另外值得一提的是,refs 并不是类组件的专属,函数式组件同样能够利用闭包暂存其值:

function CustomForm ({handleSubmit}) {
  let inputElement
  return (
    
handleSubmit(inputElement.value)}> inputElement = input} />
) }

参与互动

12. setState 和 replaceState 的区别

参考答案:

  1. setState 是修改其中的部分状态,相当于 Object. assign,只是覆盖,不会减少原来的状态;

  2. replaceState 是完全替换原来的状态,相当于赋值,将原来的 state 替换为另一个对象,如果新状态属性减少,那么 state 中就没有这个状态了。

参与互动

13. redux 有什么缺点

参考答案:

  • 一个组件所需要的数据,必须由父组件传过来,而不能像 flux 中直接从 store 取。

  • 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会有效率影响,或者需要写复杂的 shouldComponentUpdate 进行判断。

参与互动

14. 简述 flux 思想

参考答案:Flux 的最大特点,就是数据的"单向流动"。

  1. 用户访问 View

  2. View 发出用户的 Action

  3. Dispatcher 收到 Action,要求 Store 进行相应的更新

  4. Store 更新后,发出一个"change"事件

  5. View 收到"change"事件后,更新页面

参考

参与互动

15. 了解 redux 么,说一下 redux 吧

参考答案:

1、为什么要用redux

在React中,数据在组件中是单向流动的,数据从一个方向父组件流向子组件(通过props), 所以,两个非父子组件之间通信就相对麻烦,redux的出现就是为了解决state里面的数据问题

2、Redux设计理念

Redux是将整个应用状态存储到一个地方上称为store, 里面保存着一个状态树store tree, 组件可以派发(dispatch)行为(action)给store, 而不是直接通知其他组件,组件内部通过订阅store中的状态state来刷新自己的视图。

react面试题_第1张图片

3、Redux三大原则

  1. 唯一数据源

整个应用的state都被存储到一个状态树里面,并且这个状态树,只存在于唯一的store中

  1. 保持只读状态

state是只读的,唯一改变state的方法就是触发action,action是一个用于描述以发生时间的普通对象

  1. 数据改变只能通过纯函数来执行

使用纯函数来执行修改,为了描述action如何改变state的,你需要编写reducers

4、Redux概念解析

  1. Store

  • store就是保存数据的地方,你可以把它看成一个数据,整个应用只能有一个store

  • Redux提供createStore这个函数,用来生成Store

import {
    createStore
} from 'redux'
const store = createStore(fn);
  1. State

state就是store里面存储的数据,store里面可以拥有多个state,Redux规定一个state对应一个View, 只要state相同,view就是一样的,反过来也是一样的,可以通过store. getState( )获取

import {
    createStore
} from 'redux'
const store = createStore(fn);
const state = store.getState()
  1. Action

state的改变会导致View的变化,但是在redux中不能直接操作state也就是说不能使用this. setState来操作,用户只能接触到View。在Redux中提供了一个对象来告诉Store需要改变state。Action是一个对象其中type属性是必须的,表示Action的名称,其他的可以根据需求自由设置。

const action = {
    type: 'ADD_TODO',
    payload: 'redux原理'
}

在上面代码中,Action的名称是ADD_TODO,携带的数据是字符串‘redux原理’,Action描述当前发生的事情,这是改变state的唯一的方式

  1. store. dispatch( )

store. dispatch( )是view发出Action的唯一办法

store.dispatch({
    type: 'ADD_TODO',
    payload: 'redux原理'
})

store. dispatch接收一个Action作为参数,将它发送给store通知store来改变state。

  1. Reducer

Store收到Action以后,必须给出一个新的state,这样view才会发生变化。这种state的计算过程就叫做Reducer。 Reducer是一个纯函数,他接收Action和当前state作为参数,返回一个新的state

注意:Reducer必须是一个纯函数,也就是说函数返回的结果必须由参数state和action决定,而且不产生任何副作用也不能修改state和action对象

const reducer = (state, action) => {
    switch (action.type) {
        case ADD_TODO:
            return newstate;
        default
        return state
    }
}

5、Redux源码

let createStore = (reducer) => {
    let state;
    //获取状态对象
    //存放所有的监听函数
    let listeners = [];
    let getState = () => state;
    //提供一个方法供外部调用派发action
    let dispath = (action) => {
        //调用管理员reducer得到新的state
        state = reducer(state, action);
        //执行所有的监听函数
        listeners.forEach((l) => l())
    }
    //订阅状态变化事件,当状态改变发生之后执行监听函数
    let subscribe = (listener) => {
        listeners.push(listener);
    }
    dispath();
    return {
        getState,
        dispath,
        subscribe
    }
}
let combineReducers = (renducers) => {
    //传入一个renducers管理组,返回的是一个renducer
    return function(state = {}, action = {}) {
        let newState = {};
        for (var attr in renducers) {
            newState[attr] = renducers[attr](state[attr], action)

        }
        return newState;
    }
}
export {
    createStore,
    combineReducers
};

6、Redux使用案例

html代码

js代码

function createStore(reducer) {
    var state;
    var listeners = [];
    var getState = () => state;
    var dispatch = (action) => {
        state = reducer(state, action);
        listeners.forEach(l => l());
    }
    var subscribe = (listener) => {
        listeners.push(listener);
        return () => {
            listeners = listeners.filter((l) => l != listener)
        }
    }
    dispatch();
    return {
        getState,
        dispatch,
        subscribe
    }
}
var reducer = (state = 0, action) => {
    if (!action) return state;
    console.log(action);
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DECREMENT':
            return state - 1;
        default:
            return state;
    }
}
var store = createStore(reducer);
store.subscribe(function() {
    document.querySelector('#counter').innerHTML = store.getState();
});

document.querySelector('#addBtn').addEventListener('click', function() {
    store.dispatch({
        type: 'INCREMENT'
    });
});
document.querySelector('#minusBtn').addEventListener('click', function() {
    store.dispatch({
        type: 'DECREMENT'
    });
});

参考 参与互动

16. React 中有三种构建组件的方式

参考答案:React. createClass()、ES6 class 和无状态函数。

参与互动

17. react 组件的划分业务组件技术组件?

参考答案:

  • 根据组件的职责通常把组件分为 UI 组件和容器组件。

  • UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。

  • 两者通过 React-Redux 提供 connect 方法联系起来。

参与互动

18. 描述事件在 React 中的处理方式

参考答案:

为了解决跨浏览器兼容性问题,您的 React 中的事件处理程序将传递 SyntheticEvent 的实例,它是 React 的浏览器本机事件的跨浏览器包装器。

这些 SyntheticEvent 与您习惯的原生事件具有相同的接口,除了它们在所有浏览器中都兼容。有趣的是,React 实际上并没有将事件附加到子节点本身。React 将使用单个事件监听器监听顶层的所有事件。这对于性能是有好处的,这也意味着在更新 DOM 时,React 不需要担心跟踪事件监听器。

参与互动

19. 应该在 React 组件的何处发起 Ajax 请求

参考答案:

在 React 组件中,应该在 componentDidMount 中发起网络请求。这个方法会在组件第一次“挂载”(被添加到 DOM)时执行,在组件的生命周期中仅会执行一次。更重要的是,你不能保证在组件挂载之前 Ajax 请求已经完成,如果是这样,也就意味着你将尝试在一个未挂载的组件上调用 setState,这将不起作用。在 componentDidMount 中发起网络请求将保证这有一个组件可以更新了。

参与互动

20. (在构造函数中)调用 super(props) 的目的是什么

参考答案:

在 super() 被调用之前,子类是不能使用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super()。传递 props 给 super() 的原因则是便于(在子类中)能在 constructor 访问 this. props。

参与互动

21. 除了在构造函数中绑定 this,还有其它方式吗

参考答案:

你可以使用属性初始值设定项(property initializers)来正确绑定回调,create-react-app 也是默认支持的。在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。

参与互动

22. 为什么建议传递给 setState 的参数是一个 callback 而不是一个对象

参考答案:

因为 this. props 和 this. state 的更新可能是异步的,不能依赖它们的值去计算下一个 state。

参与互动

23. 何为高阶组件(higher order component)

参考答案:

高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。

参与互动

24. 何为受控组件(controlled component)

参考答案:

在 HTML 中,类似 ,