面试技巧

关注公众号 jb51net

关闭
IT专业知识 > IT职场规划 > 面试技巧 >

React面试题小结(附答案)

蓝莓圣代

如何看待前端框架选型

对于同一个类型的项目,采用开发模式,使用的基本框架都是一致的。

前端技术选型:

(1)外部用户的PC站;

(2)外部用户的mobile站;

(3)外部用户的Native App开发;

(4)内部员工的管理后台

1、外部用户的PC站

需要有SEO,有加载体验,采用的是前后端分离开发模式,页面直接渲染,基于jquery。

为什么使用jquery?

(1)主要是为了兼容IE8;

(2)是外部用户,视觉体验高,权重高。适合先有行,再有行。就是说视觉和行为要尽可能分离,会牺牲一点开发成本,但是用户更重要。

(3)绝大多数页面交互轻量用不上数据驱动。

2、外部用户的Mobile站

这里说的Mobile站主要是浏览器访问为主的,因此,页面切换都是传统连接跳转,属于传统web应用,前后端分离开发模式,页面直接渲染,采用react。大致原因:使用react 是为了 和APP端的react native保持同步。

3、外部用户的Native App开发

前端组有直接参与 Native APP 开发的项目,使用的是 React Native 进行开发。

为啥选择RN,之前Hybrid模式开发有性能优化瓶颈,采用React Native性能可以突破这个瓶颈,有原生的性能,且支持热更新,上手不算太难,跨平台,IOS和android代码复用率90%。适合交互和动画不太复杂的项目,最终要根据项目来。特别适合快速迭代的项目或者前期需要大量试错的项目。

(1)不要随意使用第三方库,后期修改维护不方便,尽量自己写还是自己写;

(2)前期还是需要客户端帮忙配合,项目搭建。

4、内部员工的管理后台

前后端分离开发,页面侧重异步渲染,使用vuejs。

大致内容是:后台管理系统有大量的增删改查操作,适合具有双向绑定的类库或者框架进行渲染。同时没有兼容性的要求(SEO,首屏渲染),因此单页面是合适的。可以选择vue,react,angular。因为vue对api,文档对开发者更友好。选用好的UI组件,规范贯彻,拆分和按需加载,自动化测试有待加强。

对于比较正式的项目,前端技术选型策略一定是产品收益最大化,用户在首位

react生命周期

react 生命周期函数 初始化阶段:

getDefaultProps:获取实例的默认属性

getInitialState:获取每个实例的初始化状态

componentWillMount:组件即将被装载、渲染到页面上

render:组件在这里生成虚拟的 DOM 节点

componentDidMount:组件真正在被装载之后 运行中状态:

componentWillReceiveProps:组件将要接收到属性的时候调用

shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)

componentWillUpdate:组件即将更新不能修改属性和状态

render:组件重新描绘

componentDidUpdate:组件已经更新

销毁阶段: componentWillUnmount:组件即将销毁

react 的虚拟dom是怎么实现的?diff算法?

首先说说为什么要使用Virturl DOM,因为操作真实DOM的耗费的性能代价太高,所以react内部使用js实现了一套dom结构,在每次操作在和真实dom之前,使用实现好的diff算法,对虚拟dom进行比较,递归找出有变化的dom节点,然后对其进行更新操作。为了实现虚拟DOM,我们需要把每一种节点类型抽象成对象,每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型,假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点。

diff算法实现

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

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

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

合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制. 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

React 中 refs 的作用是什么?

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

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

React.createClass()、ES6 class 和无状态函数。

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

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

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

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

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

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

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

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

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

react 的渲染过程中,兄弟节点之间是怎么处理的?也就是key值不一样的时候。

通常我们输出节点的时候都是map一个数组然后返回一个ReactNode,为了方便react内部进行优化,我们必须给每一个reactNode添加key,这个key prop在设计值处不是给开发者用的,而是给react用的,大概的作用就是给每一个reactNode添加一个身份标识,方便react进行识别,在重渲染过程中,如果key一样,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新,如果key不一样,则react先销毁该组件,然后重新创建该组件。

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

调用 setState 之后发生了什么?
在代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

shouldComponentUpdate 是做什么的,(react 性能优化是哪个周期函数?)
shouldComponentUpdate 这个方法用来判断是否需要调用 render 方法重新描绘 dom。因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。

为什么虚拟 dom 会提高性能?(必考)
虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。

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

展示组件(Presentational component)和容器组件(Container component)之间有何不同
展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。

容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。

类组件(Class component)和函数式组件(Functional component)之间有何不同
类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问 store 并维持状态

当组件仅是接收 props,并将组件自身渲染到页面时,该组件就是一个 '无状态组件(stateless component)',可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件

(组件的)状态(state)和属性(props)之间有何不同
State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。

Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能改变自身的 props,但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据--回调函数也可以通过 props 传递。

何为受控组件(controlled component)
在 HTML 中,类似 input, textarea 和 select> 这样的表单元素会维护自身的状态,并基于用户的输入来更新。当用户提交表单时,前面提到的元素的值将随表单一起被发送。但在 React 中会有些不同,包含表单元素的组件将会在 state 中追踪输入的值,并且每次调用回调函数时,如 onChange 会更新 state,重新渲染组件。一个输入表单元素,它的值通过 React 的这种方式来控制,这样的元素就被称为"受控元素"。

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

为什么建议传递给 setState 的参数是一个 callback 而不是一个对象
因为 this.props 和 this.state 的更新可能是异步的,不能依赖它们的值去计算下一个 state。

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

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

createElement 和 cloneElement 有什么区别?
React.createElement():JSX 语法就是用 React.createElement()来构建 React 元素的。它接受三个参数,第一个参数可以是一个标签名。如 div、span,或者 React 组件。第二个参数为传入的属性。第三个以及之后的参数,皆作为组件的子组件。

React.cloneElement()与 React.createElement()相似,不同的是它传入的第一个参数是一个 React 元素,而不是标签名或组件。新添加的属性会并入原有的属性,传入到返回的新元素中,而就的子元素奖杯替换。

简述 flux 思想
Flux 的最大特点,就是数据的"单向流动"。

用户访问 View

View 发出用户的 Action

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

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

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

React 项目用过什么脚手架
creat-react-app

了解 redux 么,说一下 redux 把
redux 是一个应用数据流框架,主要是解决了组件间状态共享的问题,原理是集中式管理,主要有三个核心方法,action,store,reducer,工作流程是 view 调用 store 的 dispatch 接收 action 传入 store,reducer 进行 state 操作,view 通过 store 提供的 getState 获取最新的数据,flux 也是用来进行数据操作的,有四个组成部分 action,dispatch,view,store,工作流程是 view 发出一个 action,派发器接收 action,让 store 进行数据更新,更新完成以后 store 发出 change,view 接受 change 更新视图。Redux 和 Flux 很像。主要区别在于 Flux 有多个可以改变应用状态的 store,在 Flux 中 dispatcher 被用来传递数据到注册的回调事件,但是在 redux 中只能定义一个可更新状态的 store,redux 把 store 和 Dispatcher 合并,结构更加简单清晰

新增 state,对状态的管理更加明确,通过 redux,流程更加规范了,减少手动编码量,提高了编码效率,同时缺点时当数据更新时有时候组件不需要,但是也要重新绘制,有些影响效率。一般情况下,我们在构建多交互,多数据流的复杂项目应用时才会使用它们

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

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

我现在有一个数组[1,2,3,4],请实现算法,得到这个数组的全排列的数组,如[2,1,3,4],[2,1,4,3]。。。。你这个算法的时间复杂度是多少
将每一个数组拆除俩个小数组进行求它的全排列,然后得到的结果互相之间又进行全排列,然后把最后的结果连接起来

你说一下webpack的一些plugin,怎么使用webpack对项目进行优化
构建优化 1、减少编译体积 ContextReplacementPugin、IgnorePlugin、babel-plugin-import、babel-plugin-transform-runtime。

2、并行编译 happypack、thread-loader、uglifyjsWebpackPlugin开启并行

3、缓存 cache-loader、hard-source-webpack-plugin、uglifyjsWebpackPlugin开启缓存、babel-loader开启缓存

4、预编译 dllWebpackPlugin && DllReferencePlugin、auto-dll-webapck-plugin

性能优化 1、减少编译体积 Tree-shaking、Scope Hositing。

2、hash缓存 webpack-md5-plugin

3、拆包 splitChunksPlugin、import()、require.ensure

说说从输入URL到看到页面发生的全过程,越详细越好
1、首先浏览器主进程接管,开了一个下载线程。

2、然后进行HTTP请求(DNS查询、IP寻址等等),中间会有三次捂手,等待响应,开始下载响应报文。

3、将下载完的内容转交给Renderer进程管理。

4、Renderer进程开始解析css rule tree和dom tree,这两个过程是并行的,所以一般我会把link标签放在页面顶部。

5、解析绘制过程中,当浏览器遇到link标签或者script、img等标签,浏览器会去下载这些内容,遇到时候缓存的使用缓存,不适用缓存的重新下载资源。

6、css rule tree和dom tree生成完了之后,开始合成render

7、tree,这个时候浏览器会进行layout,开始计算每一个节点的位置,然后进行绘制。 绘制结束后,关闭TCP连接,过程有四次挥手

你刚刚说了三次握手,四次挥手,那你描述一下?
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。 (2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。 (3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。 (2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。 (3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。 (4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

CSS中几种垂直水平居中的方式
1、绝对定位+margin:auto

2、绝对定位+margin反向偏移

3、绝对定位+transform反向偏移

4、display:tabel

5、display: flex

react和vue的比较
观察者模式实现 ?
http报文头部有哪些字段? 有什么意义 ?
请求头(Request):

Accept:text/html application/xml 告诉服务器客户端浏览器这边可以出里什么数据;

Accept-Encodeing:gzip 告诉服务器我能支持什么样的压缩格式

accept-language:告诉服务器浏览器支持的语言

Cache-control:告诉服务器是否缓存

Connection:keep-alive 告诉服务器当前保持活跃(与服务器处于链接状态)

Host:远程服务器的域名

User-agent:客户端的一些信息,浏览器信息 版本

referer:当前页面上一个页面地址。一般用于服务器判断是否为同一个域名下的请求

返回头(response-header):

cache-control:private/no-cache; 私有的不需要缓存/no-cache也不需要缓存

connection:keep-live; 服务器同意保持连接

content-Enconding:gzip;除去头的剩余部分压缩返回格式

content-length:内容长度

content-type:text/css;返回内容支持格式

Date: 时间

server:ngnix 服务器类型

set-Cookie:服务器向客户端设置cookie 第一次访问服务器会下发cookie当作身份认证信息,第二次访问服务器再把cookie送给服务器,可以当作认证信息

last-modified: 时间戳 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。

expires 告诉浏览器把回送的资源缓存多长时间 -1或0则是不缓存

etag:版本专有的加密指纹。(有的网站不用,并非必须)优先检查etag再检查last-modif

ied的时间戳。向服务器请求带if-none-match,服务器判断是否过期未过期返回304,过期返回200

// 注释:第一次请求出来的数据先进行缓存协商,是否缓存expires,cache-control 缓存时间,etag,last-modified等 //注释:多次访问的时候,浏览器先判断是否有缓存,是否过期 //未过期:直接从缓存中读取。

//http状态码

//1.指示信息–表示请求已经接受,继续处理 //2.成功–表示请求已经被成功接受,理解 //3.重定向–表示完成请求必须进行更进一步操作 //4.客户端错误–请求有语法错误或者请求无法实现 //5.服务器端错误–服务器未能实现合法的请求

移动端高清方案如何解决 ?
webpack的原理, loader 和 plugin 是干什么的? 有自己手写过么 ?

1.2 打包原理

识别入口文件

通过逐层识别模块依赖。(Commonjs、amd或者es6的import,webpack都会对其进行分析。来获取代码的依赖)

webpack做的就是分析代码。转换代码,编译代码,输出代码 最终形成打包后的代码

loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中

在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果

SSR 和 客户端渲染有什么区别 , vue是如何实现绑定事件的 ?
客户端渲染和服务器端渲染的最重要的区别就是究竟是谁来完成html文件的完整拼接,如果是在服务器端完成的,然后返回给客户端,就是服务器端渲染,而如果是前端做了更多的工作完成了html的拼接,则就是客户端渲染。

服务器端渲染的优缺点是?

优点:

前端耗时少。因为后端拼接完了html,浏览器只需要直接渲染出来。 有利于SEO。因为在后端有完整的html页面,所以爬虫更容易爬取获得信息,更有利于seo。 无需占用客户端资源。即解析模板的工作完全交由后端来做,客户端只要解析标准的html页面即可,这样对于客户端的资源占用更少,尤其是移动端,也可以更省电。 后端生成静态化文件。即生成缓存片段,这样就可以减少数据库查询浪费的时间了,且对于数据变化不大的页面非常高效 。 缺点:

不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,则对于前端复杂度高的项目,不利于项目高效开发。另外,如果是服务器端渲染,则前端一般就是写一个静态html文件,然后后端再修改为模板,这样是非常低效的,并且还常常需要前后端共同完成修改的动作; 或者是前端直接完成html模板,然后交由后端。另外,如果后端改了模板,前端还需要根据改动的模板再调节css,这样使得前后端联调的时间增加。 占用服务器端资源。即服务器端完成html模板的解析,如果请求较多,会对服务器造成一定的访问压力。而如果使用前端渲染,就是把这些解析的压力分摊了前端,而这里确实完全交给了一个服务器。

客户端渲染的优缺点是? 优点:

前后端分离。前端专注于前端UI,后端专注于api开发,且前端有更多的选择性,而不需要遵循后端特定的模板。 体验更好。比如,我们将网站做成SPA或者部分内容做成SPA,这样,尤其是移动端,可以使体验更接近于原生app。 缺点:

前端响应较慢。如果是客户端渲染,前端还要进行拼接字符串的过程,需要耗费额外的时间,不如服务器端渲染速度快。 不利于SEO。目前比如百度、谷歌的爬虫对于SPA都是不认的,只是记录了一个页面,所以SEO很差。因为服务器端可能没有保存完整的html,而是前端通过js进行dom的拼接,那么爬虫无法爬取信息。 除非搜索引擎的seo可以增加对于JavaScript的爬取能力,这才能保证seo。

移动端rem布局如何实现? 简述原理?
原理是,先按定高宽设计出来页面,然后转换为rem单位,

配合js查询屏幕大小来改变html的font-size,

最终做出所谓的完美自适应。

rem+js是宽度自适应,无法做到高度自适应,所以那些对高度要求很高的rem+js无法实现。

改变浏览器宽度,你会发现,页面所有元素的高宽都等比例缩放,

也就是大屏幕下导航是横的,小屏幕下还是横的只不过变小了。。

优点:理想状态是所有屏幕的高宽比和最初的设计高宽比一样,或者相差不多,完美适应。

缺点:碰到重视高度的设计,或者重视元素间间距的设计,那就玩不开了。

DOM节点类型

到此这篇关于React面试题小结的文章就介绍到这了,更多相关React面试题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!