如下面试题来源于github项目前端面试指南,那里有超过200道高频前端面试题及答案,目前拥有1400star.前端
为何选择使用框架而不是原生?
框架的好处:react
- 组件化: 其中以 React 的组件化最为完全,甚至能够到函数级别的原子组件,高度的组件化能够是咱们的工程易于维护、易于组合拓展。
- 自然分层: JQuery 时代的代码大部分状况下是面条代码,耦合严重,现代框架无论是 MVC、MVP仍是MVVM 模式都能帮助咱们进行分层,代码解耦更易于读写。
- 生态: 如今主流前端框架都自带生态,无论是数据流管理架构仍是 UI 库都有成熟的解决方案。
- 开发效率: 现代前端框架都默认自动更新DOM,而非咱们手动操做,解放了开发者的手动DOM成本,提升开发效率,从根本上解决了UI 与状态同步问题.
虚拟DOM的优劣如何?
优势:git
- 保证性能下限: 虚拟DOM能够通过diff找出最小差别,而后批量进行patch,这种操做虽然比不上手动优化,可是比起粗暴的DOM操做性能要好不少,所以虚拟DOM能够保证性能下限
- 无需手动操做DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,咱们无需手动操做DOM,极大提升开发效率
- 跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM能够进行更方便地跨平台操做,例如服务器渲染、移动端开发等等
缺点:程序员
- 没法进行极致优化: 在一些性能要求极高的应用中虚拟DOM没法进行针对性的极致优化,好比VScode采用直接手动操做DOM的方式进行极端的性能优化
虚拟DOM实现原理?
- 虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
- 状态变动时,记录新树和旧树的差别
- 最后把差别更新到真正的dom中
虚拟DOM原理github
React最新的生命周期是怎样的?
React 16以后有三个生命周期被废弃(但并未删除)面试
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate
官方计划在17版本彻底删除这三个函数,只保留UNSAVE_前缀的三个函数,目的是为了向下兼容,可是对于开发者而言应该尽可能避免使用他们,而是使用新增的生命周期函数替代它们算法
目前React 16.8 +的生命周期分为三个阶段,分别是挂载阶段、更新阶段、卸载阶段npm
挂载阶段:编程
- constructor: 构造函数,最早被执行,咱们一般在构造函数里初始化state对象或者给自定义方法绑定this
- getDerivedStateFromProps:
static getDerivedStateFromProps(nextProps, prevState)
,这是个静态方法,当咱们接收到新的属性想去修改咱们state,可使用getDerivedStateFromProps
- render: render函数是纯函数,只返回须要渲染的东西,不该该包含其它的业务逻辑,能够返回原生的DOM、React组件、Fragment、Portals、字符串和数字、Boolean和null等内容
- componentDidMount: 组件装载以后调用,此时咱们能够获取到DOM节点并操做,好比对canvas,svg的操做,服务器请求,订阅均可以写在这个里面,可是记得在componentWillUnmount中取消订阅
更新阶段:redux
- getDerivedStateFromProps: 此方法在更新个挂载阶段均可能会调用
- shouldComponentUpdate:
shouldComponentUpdate(nextProps, nextState)
,有两个参数nextProps和nextState,表示新的属性和变化以后的state,返回一个布尔值,true表示会触发从新渲染,false表示不会触发从新渲染,默认返回true,咱们一般利用今生命周期来优化React程序性能
- render: 更新阶段也会触发今生命周期
- getSnapshotBeforeUpdate:
getSnapshotBeforeUpdate(prevProps, prevState)
,这个方法在render以后,componentDidUpdate以前调用,有两个参数prevProps和prevState,表示以前的属性和以前的state,这个函数有一个返回值,会做为第三个参数传给componentDidUpdate,若是你不想要返回值,能够返回null,今生命周期必须与componentDidUpdate搭配使用
- componentDidUpdate:
componentDidUpdate(prevProps, prevState, snapshot)
,该方法在getSnapshotBeforeUpdate方法以后被调用,有三个参数prevProps,prevState,snapshot,表示以前的props,以前的state,和snapshot。第三个参数是getSnapshotBeforeUpdate返回的,若是触发某些回调函数时须要用到 DOM 元素的状态,则将对比或计算的过程迁移至 getSnapshotBeforeUpdate,而后在 componentDidUpdate 中统一触发回调或更新状态。
卸载阶段:
- componentWillUnmount: 当咱们的组件被卸载或者销毁了就会调用,咱们能够在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工做
一个查看react生命周期的网站
React的请求应该放在哪一个生命周期中?
React的异步请求到底应该放在哪一个生命周期里,有人认为在componentWillMount
中能够提早进行异步请求,避免白屏,其实这个观点是有问题的.
因为JavaScript中异步事件的性质,当您启动API调用时,浏览器会在此期间返回执行其余工做。当React渲染一个组件时,它不会等待componentWillMount它完成任何事情 - React继续前进并继续render,没有办法“暂停”渲染以等待数据到达。
并且在componentWillMount
请求会有一系列潜在的问题,首先,在服务器渲染时,若是在 componentWillMount 里获取数据,fetch data会执行两次,一次在服务端一次在客户端,这形成了多余的请求,其次,在React 16进行React Fiber重写后,componentWillMount
可能在一次渲染中屡次调用.
目前官方推荐的异步请求是在componentDidmount
中进行.
若是有特殊需求须要提早请求,也能够在特殊状况下在constructor
中请求:
react 17以后componentWillMount
会被废弃,仅仅保留UNSAFE_componentWillMount
setState究竟是异步仍是同步?
先给出答案: 有时表现出异步,有时表现出同步
setState
只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout
中都是同步的。
setState
的“异步”并非说内部由异步代码实现,其实自己执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新以前,致使在合成事件和钩子函数中无法立马拿到更新后的值,造成了所谓的“异步”,固然能够经过第二个参数 setState(partialState, callback)
中的callback
拿到更新后的结果。
setState
的批量更新优化也是创建在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中若是对同一个值进行屡次setState
,setState
的批量更新策略会对其进行覆盖,取最后一次的执行,若是是同时setState
多个不一样的值,在更新时会对其进行合并批量更新。
React组件通讯如何实现?
React组件间通讯方式:
- 父组件向子组件通信: 父组件能够向子组件经过传 props 的方式,向子组件进行通信
- 子组件向父组件通信: props+回调的方式,父组件向子组件传递props进行通信,此props为做用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,做为参数,传递到父组件的做用域中
- 兄弟组件通讯: 找到这两个兄弟节点共同的父节点,结合上面两种方式由父节点转发信息进行通讯
- 跨层级通讯:
Context
设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言,对于跨越多层的全局数据经过Context
通讯再适合不过
- 发布订阅模式: 发布者发布事件,订阅者监听事件并作出反应,咱们能够经过引入event模块进行通讯
- 全局状态管理工具: 借助Redux或者Mobx等全局状态管理工具进行通讯,这种工具会维护一个全局状态中心Store,并根据不一样的事件产生新的状态
React有哪些优化性能是手段?
性能优化的手段不少时候是通用的详情见前端性能优化加载篇
React如何进行组件/逻辑复用?
抛开已经被官方弃用的Mixin,组件抽象的技术目前有三种比较主流:
组件复用详解见组件复用
mixin、hoc、render props、react-hooks的优劣如何?
Mixin的缺陷:
-
组件与 Mixin 之间存在隐式依赖(Mixin 常常依赖组件的特定方法,但在定义组件时并不知道这种依赖关系)
-
多个 Mixin 之间可能产生冲突(好比定义了相同的state字段)
-
Mixin 倾向于增长更多状态,这下降了应用的可预测性(The more state in your application, the harder it is to reason about it.),致使复杂度剧增
-
隐式依赖致使依赖关系不透明,维护成本和理解成本迅速攀升:
-
难以快速理解组件行为,须要全盘了解全部依赖 Mixin 的扩展行为,及其之间的相互影响
-
组价自身的方法和state字段不敢轻易删改,由于难以肯定有没有 Mixin 依赖它
-
Mixin 也难以维护,由于 Mixin 逻辑最后会被打平合并到一块儿,很难搞清楚一个 Mixin 的输入输出
HOC相比Mixin的优点:
- HOC经过外层组件经过 Props 影响内层组件的状态,而不是直接改变其 State不存在冲突和互相干扰,这就下降了耦合度
- 不一样于 Mixin 的打平+合并,HOC 具备自然的层级结构(组件树结构),这又下降了复杂度
HOC的缺陷:
- 扩展性限制: HOC 没法从外部访问子组件的 State所以没法经过shouldComponentUpdate滤掉没必要要的更新,React 在支持 ES6 Class 以后提供了React.PureComponent来解决这个问题
- Ref 传递问题: Ref 被隔断,后来的React.forwardRef 来解决这个问题
- Wrapper Hell: HOC可能出现多层包裹组件的状况,多层抽象一样增长了复杂度和理解成本
- 命名冲突: 若是高阶组件屡次嵌套,没有使用命名空间的话会产生冲突,而后覆盖老属性
- 不可见性: HOC至关于在原有组件外层再包装一个组件,你压根不知道外层的包装是啥,对于你是黑盒
Render Props优势:
- 上述HOC的缺点Render Props均可以解决
Render Props缺陷:
- 使用繁琐: HOC使用只须要借助装饰器语法一般一行代码就能够进行复用,Render Props没法作到如此简单
- 嵌套过深: Render Props虽然摆脱了组件多层嵌套的问题,可是转化为了函数回调的嵌套
React Hooks优势:
- 简洁: React Hooks解决了HOC和Render Props的嵌套问题,更加简洁
- 解耦: React Hooks能够更方便地把 UI 和状态分离,作到更完全的解耦
- 组合: Hooks 中能够引用另外的 Hooks造成新的Hooks,组合变化万千
- 函数友好: React Hooks为函数组件而生,从而解决了类组件的几大问题:
- this 指向容易错误
- 分割在不一样声明周期中的逻辑使得代码难以理解和维护
- 代码复用成本高(高阶组件容易使代码量剧增)
React Hooks缺陷:
-
额外的学习成本(Functional Component 与 Class Component 之间的困惑)
-
写法上有限制(不能出如今条件、循环中),而且写法限制增长了重构成本
-
破坏了PureComponent、React.memo浅比较的性能优化效果(为了取最新的props和state,每次render()都要从新建立事件处函数)
-
在闭包场景可能会引用到旧的state、props值
-
内部实现上不直观(依赖一份可变的全局状态,再也不那么“纯”)
-
React.memo并不能彻底替代shouldComponentUpdate(由于拿不到 state change,只针对 props change)
关于react-hooks的评价来源于官方react-hooks RFC
你是如何理解fiber的?
React Fiber 是一种基于浏览器的单线程调度算法.
React 16以前 ,reconcilation
算法其实是递归,想要中断递归是很困难的,React 16 开始使用了循环来代替以前的递归.
Fiber
:一种将 recocilation
(递归 diff),拆分红无数个小任务的算法;它随时可以中止,恢复。中止恢复的时机取决于当前的一帧(16ms)内,还有没有足够的时间容许计算。
Fiber 详解
你对 Time Slice的理解?
时间分片
- React 在渲染(render)的时候,不会阻塞如今的线程
- 若是你的设备足够快,你会感受渲染是同步的
- 若是你设备很是慢,你会感受还算是灵敏的
- 虽然是异步渲染,可是你将会看到完整的渲染,而不是一个组件一行行的渲染出来
- 一样书写组件的方式
也就是说,这是React背后在作的事情,对于咱们开发者来讲,是透明的,具体是什么样的效果呢?
有图表三个图表,有一个输入框,以及上面的三种模式
这个组件很是的巨大,并且在输入框每次**输入东西的时候,就会进去一直在渲染。**为了更好的看到渲染的性能,Dan为咱们作了一个表。
咱们先看看,同步模式:
同步模式下,咱们都知道,咱们没输入一个字符,React就开始渲染,当React渲染一颗巨大的树的时候,是很是卡的,因此才会有shouldUpdate的出现,在这里Dan也展现了,这种卡!
咱们再来看看第二种(Debounced模式):
Debounced模式简单的来讲,就是延迟渲染,好比,当你输入完成之后,再开始渲染全部的变化。
这么作的坏处就是,至少不会阻塞用户的输入了,可是依然有很是严重的卡顿。
切换到异步模式:
异步渲染模式就是不阻塞当前线程,继续跑。在视频里能够看到全部的输入,表上都会是原谅色的。
时间分片正是基于可随时打断、重启的Fiber架构,可打断当前任务,优先处理紧急且重要的任务,保证页面的流畅运行.
redux的工做流程?
首先,咱们看下几个核心概念:
- Store:保存数据的地方,你能够把它当作一个容器,整个应用只能有一个Store。
- State:Store对象包含全部数据,若是想获得某个时点的数据,就要对Store生成快照,这种时点的数据集合,就叫作State。
- Action:State的变化,会致使View的变化。可是,用户接触不到State,只能接触到View。因此,State的变化必须是View致使的。Action就是View发出的通知,表示State应该要发生变化了。
- Action Creator:View要发送多少种消息,就会有多少种Action。若是都手写,会很麻烦,因此咱们定义一个函数来生成Action,这个函数就叫Action Creator。
- Reducer:Store收到Action之后,必须给出一个新的State,这样View才会发生变化。这种State的计算过程就叫作Reducer。Reducer是一个函数,它接受Action和当前State做为参数,返回一个新的State。
- dispatch:是View发出Action的惟一方法。
而后咱们过下整个工做流程:
- 首先,用户(经过View)发出Action,发出方式就用到了dispatch方法。
- 而后,Store自动调用Reducer,而且传入两个参数:当前State和收到的Action,Reducer会返回新的State
- State一旦有变化,Store就会调用监听函数,来更新View。
到这儿为止,一次用户交互流程结束。能够看到,在整个流程中数据都是单向流动的,这种方式保证了流程的清晰。
redux原理详解
react-redux是如何工做的?
- Provider: Provider的做用是从最外部封装了整个应用,并向connect模块传递store
- connect: 负责链接React和Redux
- 获取state: connect经过context获取Provider中的store,经过store.getState()获取整个store tree 上全部state
- 包装原组件: 将state和action经过props的方式传入到原组件内部wrapWithConnect返回一个ReactComponent对象Connect,Connect从新render外部传入的原组件WrappedComponent,并把connect中传入的mapStateToProps, mapDispatchToProps与组件上原有的props合并后,经过属性的方式传给WrappedComponent
- 监听store tree变化: connect缓存了store tree中state的状态,经过当前state状态和变动前state状态进行比较,从而肯定是否调用
this.setState()
方法触发Connect及其子组件的从新渲染
redux与mobx的区别?
二者对比:
- redux将数据保存在单一的store中,mobx将数据保存在分散的多个store中
- redux使用plain object保存数据,须要手动处理变化后的操做;mobx适用observable保存数据,数据变化后自动处理响应的操做
- redux使用不可变状态,这意味着状态是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;mobx中的状态是可变的,能够直接对其进行修改
- mobx相对来讲比较简单,在其中有不少的抽象,mobx更多的使用面向对象的编程思惟;redux会比较复杂,由于其中的函数式编程思想掌握起来不是那么容易,同时须要借助一系列的中间件来处理异步和反作用
- mobx中有更多的抽象和封装,调试会比较困难,同时结果也难以预测;而redux提供可以进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加的容易
场景辨析:
基于以上区别,咱们能够简单得分析一下二者的不一样使用场景.
mobx更适合数据不复杂的应用: mobx难以调试,不少状态没法回溯,面对复杂度高的应用时,每每力不从心.
redux适合有回溯需求的应用: 好比一个画板应用、一个表格应用,不少时候须要撤销、重作等操做,因为redux不可变的特性,自然支持这些操做.
mobx适合短平快的项目: mobx上手简单,样板代码少,能够很大程度上提升开发效率.
固然mobx和redux也并不必定是非此即彼的关系,你也能够在项目中用redux做为全局状态管理,用mobx做为组件局部状态管理器来用.
redux中如何进行异步操做?
固然,咱们能够在componentDidmount
中直接进行请求无须借助redux.
可是在必定规模的项目中,上述方法很难进行异步流的管理,一般状况下咱们会借助redux的异步中间件进行异步处理.
redux异步流中间件其实有不少,可是当下主流的异步中间件只有两种redux-thunk、redux-saga,固然redux-observable可能也有资格占据一席之地,其他的异步中间件无论是社区活跃度仍是npm下载量都比较差了.
redux异步中间件之间的优劣?
redux-thunk优势:
- 体积小: redux-thunk的实现方式很简单,只有不到20行代码
- 使用简单: redux-thunk没有引入像redux-saga或者redux-observable额外的范式,上手简单
redux-thunk缺陷:
- 样板代码过多: 与redux自己同样,一般一个请求须要大量的代码,并且不少都是重复性质的
- 耦合严重: 异步操做与redux的action偶合在一块儿,不方便管理
- 功能孱弱: 有一些实际开发中经常使用的功能须要本身进行封装
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-observable优势:
- 功能最强: 因为背靠rxjs这个强大的响应式编程的库,借助rxjs的操做符,你能够几乎作任何你能想到的异步处理
- 背靠rxjs: 因为有rxjs的加持,若是你已经学习了rxjs,redux-observable的学习成本并不高,并且随着rxjs的升级redux-observable也会变得更强大
redux-observable缺陷:
- 学习成本奇高: 若是你不会rxjs,则须要额外学习两个复杂的库
- 社区通常: redux-observable的下载量只有redux-saga的1/5,社区也不够活跃,在复杂异步流中间件这个层面redux-saga仍处于领导地位
关于redux-saga与redux-observable的详细比较可见此连接
公众号
想要实时关注笔者最新的文章和最新的文档更新请关注公众号程序员面试官,后续的文章会优先在公众号更新.
简历模板: 关注公众号回复「模板」获取
《前端面试手册》: 配套于本指南的突击手册,关注公众号回复「fed」获取