2018年6月前端面试经历(下)

前言

这是6月前端面试最后一篇文章了,由于个人技术栈是react,下面都是面试官面对面问的一些问题的记录~css

react

react的生命周期

MOUNTING:前端

  • mountComponent 负责管理生命周期中的 getInitialState、componentWillMount、render 和 componentDidMount。

因为 getDefaultProps 是经过构造函数进行管理的,因此也是整个生命周期中最早开始执行的。 而 mountComponent 只能望洋兴叹, 没法调用到 getDefaultProps。 这就解释了为什么 getDefault-Props只执行一次。node

RECEIVE_PROPS:react

  • updateComponent 负责管理生命周期中的 componentWillReceiveProps、shouldComponent- Update、componentWillUpdate、render 和 componentDidUpdate。

UNMOUNTING:webpack

  • unmountComponent 负责管理生命周期中的 componentWillUnmount

组件的生命周期在不一样状态下的执行顺序。web

  • 当首次挂载组件时, 按顺序执行 getDefaultProps、 getInitialState、 componentWillMount、render 和 componentDidMount。
  • 当卸载组件时,执行 componentWillUnmount。
  • 从新挂载组件时,此时按顺序执行 getInitialState、componentWillMount、render 和componentDidMount,但并不执行 getDefaultProps
  • 再次渲染组件时,组件接受到更新状态,此时按顺序执行 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate

react的生命周期与setState的关系

  • setState React 初学者常会写出 this.state.value = 1 这样的代码,这是彻底错误的写法。

绝对不要直接修改 this.state,这不只是一种低效的作法,并且颇有可能会被以后的操做替换面试

setState 经过一个队列机制实现 state 更新。当执行 setState 时,会将须要更新的 state 合并后放入状态队列,而不会马上更新 this.state,队列机制能够高效地批量更新 state。若是不经过 setState 而直接修改 this.state 的值,那么该 state 将不会被放入状态队列中,当下次调用 setState 并对状态队列进行合并时,将会忽略以前直接被修改的 state,而形成没法预知的错误。算法

所以,应该使用 setState 方法来更新 state,同时 React 也正是利用状态队列机制实现了 setState 的异步更新,避免频繁地重复更新 state。编程

相关源码以下:redux

// 将新的 state 合并到状态更新队列中 
var nextState = this._processPendingState(nextProps, nextContext); 
// 根据更新队列和 shouldComponentUpdate 的状态来判断是否须要更新组件
var shouldUpdate =  this._pendingForceUpdate ||
                    !inst.shouldComponentUpdate ||
                    inst.shouldComponentUpdate(nextProps, nextState, nextContext)

复制代码
  • setState 在生命周期中的调用

WechatIMG2.jpeg-243.2kB

  • 若是咱们在 componentWillMount 中执行 setState 方法,会发生什么呢? 组件会更新 state,但组件只渲染一次。所以,这是无心义的执行,初始化时的 state 均可以放在 this.state。

  • 若是咱们在 componentDidMount 中执行 setState 方法,又会发生什么呢? 组件固然会再次更新,不过在初始化过程就渲染了两次组件,这并非一件好事。但实际状况是,有一些场景不得不须要 setState,好比计算组件的位置或宽高时,就不得不让组件先渲染,更新必要的信息后,再次渲染。

  • 若是咱们在 componentWillUnmount 中执行 setState 方法,又会发生什么呢? 不会触发 re-render 的,这是由于全部更新队列和更新状态都被重置为null,并清除了公共类,完成了组件卸载操做

  • setState 循环调用风险

当调用 setState 时, 实际上会执行 enqueueSetState 方法, 并对 partialState 以及_pending- StateQueue 更新队列进行合并操做,最终经过 enqueueUpdate 执行 state 更新。

而 performUpdateIfNecessary 方法会获取 _pendingElement、_pendingStateQueue、_pending- ForceUpdate,并调用 receiveComponent 和 updateComponent 方法进行组件更新。

若是在 shouldComponentUpdatecomponentWillUpdate 方法中调用 setState,此时 this._pendingStateQueue != null,则 performUpdateIfNecessary 方法就会调用 updateComponent 方法进行组件更新,但 updateComponent 方法又会调用 shouldComponentUpdatecomponentWill- Update 方法,所以形成循环调用,使得浏览器内存占满后崩溃.以下图

WechatIMG3.jpeg-77.2kB

pureComponent和component有什么区别

在一个父组件里有多个子组件的时候,修改一个子组件会致使全部子组件所有从新渲染。 咱们通常都会使用shouldComponentUpdate的生命周期去判断,可是这个生命周期是及其消耗性能的,在react里并不推荐使用这个方法。

这个时候咱们能够简单的去模拟一个shouldComponentUpdate的方法。pureComponent就是在component的最外层帮咱们默认实现了一个浅比较。

React.PureComponent 与 React.Component几乎彻底相同,但React.PureComponent经过prop和state的浅对比来实现 shouldComponentUpate()。

若是React组件的 render()函数在给定相同的props和state下渲染为相同的结果,在某些场景下你可使用 React.PureComponent 来提高性能。

若是对象包含复杂的数据结构,它可能会因深层的数据不一致而产生错误的否认判断(表现为对象深层的数据已改变视图却没有更新)。

考虑使用不可变对象来促进嵌套数据的快速比较。此外,React.PureComponent的shouldComponentUpate() 会忽略整个组件的子级。请确保全部的子级组件也是”Pure”的

如何在在生命周期中发送一个异步的请求

通常在componentDidMount()生命周期里去发送一个异步请求。由于这个时候可能须要的dom已经都挂载在了浏览器上,咱们能够去拿到一些咱们想要的参数,或者是把返回的数据保存在标签中。

参考一下官方对componentDidMount的解释

componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.

react的高阶组件是什么?如何实现一个高阶组件?

React 组件的构建过程当中,经常有这样的场景,有一类功能须要被不一样的组件公用,此时就涉及抽象的话题。在不一样的设计理念下,有许多的抽象方法,而针对 React,咱们会用到高阶组件。

higher-order function(高阶函数)在函数式编程中是一个基本的概念,它描述的是这样一种函数:这种函数接受函数做为输入,或是输出一个函数。好比,经常使用的工具方法 map、reduce 和 sort 等都是高阶函数。 高阶组件(higher-order component) ,相似于高阶函数,它接受 React组件做为输入,输出一个新的 React 组件。 实现高阶组件的方法有以下两种:

  • 属性代理(props proxy)。高阶组件经过被包裹的 React 组件来操做 props。
  • 反向继承(inheritance inversion)。高阶组件继承于被包裹的 React 组件。

从react推荐组件定义key的缘由。

diff 做为 Virtual DOM 的加速器,其算法上的改进优化是 React 整个界面渲染的基础和性能保障, 同时也是 React 源码中最神秘、 最难以想象的部分。 本节依然从源码入手, 深刻剖析 diff 的难以想象之处。

下面介绍 React diff 算法的 3 个策略

  • 策略一:Web UI 中 DOM 节点跨层级的移动操做特别少,能够忽略不计
  • 策略二:拥有相同类的两个组件将会生成类似的树形结构,拥有不一样类的两个组件将会生成不一样的树形结构。
  • 策略三:对于同一层级的一组子节点,它们能够经过惟一 id 进行区分。

其中策略三就是:element diff,当节点处于同一层级时,diff 提供了 3 种节点操做,分别为 INSERT_MARKUP(插入) 、MOVE_ EXISTING(移动)和 REMOVE_NODE(删除) 。

列如:旧集合中包含节点A、B、C 和 D,更新后的新集合中包含节点 B、A、D 和 C,此时新旧集合进行 diff 差别化对比,发现 B != A,则建立并插入 B 至新集合,删除旧集合 A;以此类推,建立并插入 A、D 和 C,删除 B、C 和 D。

WechatIMG4.jpeg-53.3kB

针对一些都是相同的节点,但因为位置发生变化,致使须要进行繁杂低效的删除、建立操做,其实只要对这些节点进行位置移动便可。React 提出优化策略: 容许开发者对同一层级的同组子节点, 添加惟一 key 进行区分,虽然只是小小的改动,性能上却发生了翻天覆地的变化!

新旧集合所包含的节点如图所示,进行diff差别化对比后,经过key发现新旧集合中的节点都是相同的节点, 所以无需进行节点删除和建立, 只须要将旧集合中节点的位置进行移动,更新为新集合中节点的位置,此时 React 给出的 diff 结果为:B、D 不作任何操做,A、C 进行移动操做便可。

WechatIMG5.jpeg-71.1kB

redux

redux里combineReducers

combineReducers 辅助函数的做用是,把一个由多个不一样 reducer 函数做为 value 的 object,合并成一个最终的 reducer 函数,而后就能够对这个 reducer 调用 createStore。

合并后的 reducer 能够调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。

最终,state 对象的结构会是这样的:

{
  reducer1: ...
  reducer2: ...
}
复制代码

经过为传入对象的 reducer 命名不一样来控制 state key 的命名。例如,你能够调用 combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) 将 state 结构变为 { todos, counter }。

一般的作法是命名 reducer,而后 state 再去分割那些信息,所以你可使用 ES6 的简写方法:combineReducers({ counter, todos })。这与 combineReducers({ counter: counter, todos: todos }) 同样。

Flux 用户使用须知 本函数能够帮助你组织多个 reducer,使它们分别管理自身相关联的 state。相似于 Flux 中的多个 store 分别管理不一样的 state。在 Redux 中,只有一个 store,可是 combineReducers 让你拥有多个 reducer,同时保持各自负责逻辑块的独立性。

  • 参数 reducers (Object): 一个对象,它的值(value) 对应不一样的 reducer 函数,这些 reducer 函数后面会被合并成一个。下面会介绍传入 reducer 函数须要知足的规则。

  • 返回值 (Function):一个调用 reducers 对象里全部 reducer 的 reducer,而且构造一个与 reducers 对象结构相同的 state 对象。

注意 本函数设计的时候有点偏主观,就是为了不新手犯一些常见错误。也因些咱们故意设定一些规则,但若是你本身手动编写根 redcuer 时并不须要遵照这些规则。

每一个传入 combineReducers 的 reducer 都需知足如下规则:

全部未匹配到的 action,必须把它接收到的第一个参数也就是那个 state 原封不动返回。

永远不能返回 undefined。当过早 return 时很是容易犯这个错误,为了不错误扩散,遇到这种状况时 combineReducers 会抛异常。

若是传入的 state 就是 undefined,必定要返回对应 reducer 的初始 state。根据上一条规则,初始 state 禁止使用 undefined。使用 ES6 的默认参数值语法来设置初始 state 很容易,但你也能够手动检查第一个参数是否为 undefined。

虽然 combineReducers 自动帮你检查 reducer 是否符合以上规则,但你也应该牢记,并尽可能遵照。

//模拟一个combineReducers
var combineReducers1 = function(obj){
    //内部具体代码

    var finalState = {};
    function reducer(state,action){
        //reducer具体逻辑

        for (var p in obj) {
         //根据key属性值调用function(state.属性名,action)
         finalState[p] = obj[p](state[p], action);
        }

        //返回state
        return finalState;
    }

    //返回最终的reducer
    return reducer;
}
复制代码

redux的middleware的理解

面对多样的业务场景,单纯地修改 dispatch 或 reducer 的代码显然不具备普适性,咱们须要的是能够组合的、自由插拔的插件机制,这一点 Redux 借鉴了 Koa (它是用于构建 Web 应用的 Node.js 框架)里 middleware 的思想。

Redux 中 reducer 更关心的是数据的转化逻辑,因此 middleware 就是为了加强 dispatch 而出现的。

WechatIMG6.jpeg-69.6kB

这里是middleware实现的源码

export default function applyMiddleware(...middlewares) {
    return (next) => (reducer, initialState) => {
        let store = next(reducer, initialState);
        let dispatch = store.dispatch;
        let chain = [];
        var middlewareAPI = {
            getState: store.getState,
            dispatch: (action) => dispatch(action),
        };    
        chain = middlewares.map(middleware => middleware(middlewareAPI));
        dispatch = compose(...chain)(store.dispatch);
    return {
        ...store,
        dispatch,
        };
    }
}
复制代码

源码我就不解析了,这个源码我少说看了5遍,一直理解都不到位,精髓难懂啊

WechatIMG7.jpeg-39.2kB


彩蛋

webpack和gulp的区别

webpack的打包原理是:1.一切皆是模块。给定一个主体文件(列如index.js),而后从这个主js中开始去招全部依赖的js,css,img等经过loader把他们都解析成模块输出。

与gulp有什么区别。

由于用过gulp,他的配置是任务,列task,好比js压缩,解析。相似于对less或sass的编译,组合,对图片的压缩,gulp就是提供一个流程化的一个工具。还能够在webpack前先执行gulp作一些流程化的配置,而后用gulp把webpack跑起来~

介绍几款webpack的插件

  • copy-webpack-plugin:将单个或整个目录复制到构建目录里
  • Extract-text-webpack-plugin:打包的时候分离出文本,打包到单独的文件里

在工做中遇到那些问题?如何解决的。

这个问题基本上是百分之百必问,我以为,能够在本身的项目里找一个即记忆犹新又能突出难点而且本身掌握的问题去说明~ 太简单又突出不了各位的实力,太难了还没掌握的面试官一深挖就暴露了,哈哈哈哈

很是感谢 《深刻React技术栈》做者:陈屹 这本书,让我在react的道路真的学到了不少~推荐~

友情连接:

2018年6月前端面试经历(上)

2018年6月前端面试经历(中)