腾讯前端必会 react 面试题合集
React-Router 的路由有几种模式?
React-Router 支持使用 hash(对应 HashRouter)和 browser(对应 BrowserRouter) 两种路由规则, react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现应用的 UI 和 URL 同步:
BrowserRouter 创建的 URL 格式:xxx.com/path
HashRouter 创建的 URL 格式:xxx.com/#/path
(1)BrowserRouter
它使用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来保持 UI 和 URL 的同步。由此可以看出,BrowserRouter 是使用 HTML 5 的 history API 来控制路由跳转的:
其中的属性如下:
basename 所有路由的基准 URL。basename 的正确格式是前面有一个前导斜杠,但不能有尾部斜杠;
等同于
forceRefresh 如果为 true,在导航的过程中整个页面将会刷新。一般情况下,只有在不支持 HTML5 history API 的浏览器中使用此功能;
getUserConfirmation 用于确认导航的函数,默认使用 window.confirm。例如,当从 /a 导航至 /b 时,会使用默认的 confirm 函数弹出一个提示,用户点击确定后才进行导航,否则不做任何处理;
需要配合
<Prompt>
一起使用。
KeyLength 用来设置 Location.Key 的长度。
(2)HashRouter
使用 URL 的 hash 部分(即 window.location.hash)来保持 UI 和 URL 的同步。由此可以看出,HashRouter 是通过 URL 的 hash 属性来控制路由跳转的:
其参数如下:
basename, getUserConfirmation 和
BrowserRouter
功能一样;hashType window.location.hash 使用的 hash 类型,有如下几种:
slash - 后面跟一个斜杠,例如 #/ 和 #/sunshine/lollipops;
noslash - 后面没有斜杠,例如 # 和 #sunshine/lollipops;
hashbang - Google 风格的 ajax crawlable,例如 #!/ 和 #!/sunshine/lollipops。
如何使用 4.0 版本的 React Router?
React Router 4.0 版本中对 hashHistory 做了迁移,执行包安装命令 npm install react-router-dom 后,按照如下代码进行使用即可。
componentWillReceiveProps 调用时机
已经被废弃掉
当 props 改变的时候才调用,子组件第二次接收到 props 的时候
redux 中间件
中间件提供第三方插件的模式,自定义拦截
action
->reducer
的过程。变为action
->middlewares
->reducer
。这种机制可以让我们改变数据流,实现如异步action
,action
过滤,日志输出,异常报告等功能
redux-logger
:提供日志输出redux-thunk
:处理异步操作redux-promise
:处理异步操作,actionCreator
的返回值是promise
为什么虚拟 dom 会提高性能
虚拟
dom
相当于在js
和真实dom
中间加了一个缓存,利用dom diff
算法避免了没有必要的dom
操作,从而提高性能
具体实现步骤如下
用
JavaScript
对象结构表示 DOM 树的结构;然后用这个树构建一个真正的DOM
树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把 2 所记录的差异应用到步骤 1 所构建的真正的
DOM
树上,视图就更新
虚拟 DOM 一定会提高性能吗?
很多人认为虚拟 DOM 一定会提高性能,一定会更快,其实这个说法有点片面,因为虚拟 DOM 虽然会减少 DOM 操作,但也无法避免 DOM 操作
它的优势是在于 diff 算法和批量处理策略,将所有的 DOM 操作搜集起来,一次性去改变真实的 DOM,但在首次渲染上,虚拟 DOM 会多了一层计算,消耗一些性能,所以有可能会比 html 渲染的要慢
注意,虚拟 DOM 实际上是给我们找了一条最短,最近的路径,并不是说比 DOM 操作的更快,而是路径最简单
在 ReactNative 中,如何解决 adb devices 找不到连接设备的问题?
在使用 Genymotion 时,首先需要在 SDK 的 platform-tools 中加入环境变量,然后在 Genymotion 中单击 Setting,选择 ADB 选项卡,单击 Use custom Android SDK tools,浏览本地 SDK 的位置,单击 OK 按钮就可以了。启动虛拟机后,在 cmd 中输入 adb devices 可以查看设备。
参考 前端进阶面试题详细解答
传入 setstate 函数的第二个参数的作用是什么?
第二个参数是一个函数,该函数会在 setState 函数调用完成并且组件开始重渲染时调用,可以用该函数来监听渲染是否完成。
为什么 JSX 中的组件名要以大写字母开头
因为 React 要知道当前渲染的是组件还是 HTML 元素
受控组件和非受控组件区别是啥?
受控组件是 React 控制中的组件,并且是表单数据真实的唯一来源。
非受控组件是由 DOM 处理表单数据的地方,而不是在 React 组件中。尽管非受控组件通常更易于实现,因为只需使用
refs
即可从 DOM 中获取值,但通常建议优先选择受控制的组件,而不是非受控制的组件。这样做的主要原因是受控组件支持即时字段验证,允许有条件地禁用/启用按钮,强制输入格式。
Fiber
React 的核心流程可以分为两个部分:
reconciliation
(调度算法,也可称为render
)更新
state
与props
;调用生命周期钩子;
生成
virtual dom
这里应该称为
Fiber Tree
更为符合;通过新旧 vdom 进行 diff 算法,获取 vdom change
确定是否需要重新渲染
commit
如需要,则操作
dom
节点更新
要了解 Fiber,我们首先来看为什么需要它
问题 : 随着应用变得越来越庞大,整个更新渲染的过程开始变得吃力,大量的组件渲染会导致主进程长时间被占用,导致一些动画或高频操作出现卡顿和掉帧的情况。而关键点,便是 同步阻塞。在之前的调度算法中,React 需要实例化每个类组件,生成一颗组件树,使用 同步递归 的方式进行遍历渲染,而这个过程最大的问题就是无法 暂停和恢复。
解决方案: 解决同步阻塞的方法,通常有两种: 异步 与 任务分割。而 React Fiber 便是为了实现任务分割而诞生的
简述
在
React V16
将调度算法进行了重构, 将之前的stack reconciler
重构成新版的 fiberreconciler
,变成了具有链表和指针的 单链表树遍历算法。通过指针映射,每个单元都记录着遍历当下的上一步与下一步,从而使遍历变得可以被暂停和重启这里我理解为是一种 任务分割调度算法,主要是 将原先同步更新渲染的任务分割成一个个独立的 小任务单位,根据不同的优先级,将小任务分散到浏览器的空闲时间执行,充分利用主进程的事件循环机制
核心
Fiber
这里可以具象为一个 数据结构
链表树遍历算法 : 通过 节点保存与映射,便能够随时地进行 停止和重启,这样便能达到实现任务分割的基本前提
首先通过不断遍历子节点,到树末尾;
开始通过
sibling
遍历兄弟节点;return 返回父节点,继续执行 2;
直到 root 节点后,跳出遍历;
任务分割 ,React 中的渲染更新可以分成两个阶段
reconciliation 阶段 : vdom 的数据对比,是个适合拆分的阶段,比如对比一部分树后,先暂停执行个动画调用,待完成后再回来继续比对
Commit 阶段 : 将 change list 更新到 dom 上,并不适合拆分,才能保持数据与 UI 的同步。否则可能由于阻塞 UI 更新,而导致数据更新和 UI 不一致的情况
分散执行: 任务分割后,就可以把小任务单元分散到浏览器的空闲期间去排队执行,而实现的关键是两个新 API:
requestIdleCallback
与requestAnimationFrame
低优先级的任务交给
requestIdleCallback
处理,这是个浏览器提供的事件循环空闲期的回调函数,需要pollyfill
,而且拥有deadline
参数,限制执行事件,以继续切分任务;高优先级的任务交给
requestAnimationFrame
处理;
优先级策略: 文本框输入 > 本次调度结束需完成的任务 > 动画过渡 > 交互反馈 > 数据更新 > 不会显示但以防将来会显示的任务
Fiber 其实可以算是一种编程思想,在其它语言中也有许多应用(Ruby Fiber)。
核心思想是 任务拆分和协同,主动把执行权交给主线程,使主线程有时间空挡处理其他高优先级任务。
当遇到进程阻塞的问题时,任务分割、异步调用 和 缓存策略 是三个显著的解决思路。
react-router4 的核心
路由变成了组件
分散到各个页面,不需要配置 比如
<link> <route></route>
React 中 keys 的作用是什么?
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在 React 中渲染集合时,向每个重复的元素添加关键字对于帮助 React 跟踪元素与数据之间的关联非常重要。key 应该是唯一 ID,最好是 UUID 或收集项中的其他唯一字符串:
在集合中添加和删除项目时,不使用键或将索引用作键会导致奇怪的行为。
react 中这两个生命周期会触发死循环
componentWillUpdate
生命周期在shouldComponentUpdate
返回 true 后被触发。在这两个生命周期只要视图更新就会触发,因此不能再这两个生命周期中使用 setState。否则会导致死循环
调和阶段 setState 内部干了什么
当调用 setState 时,React 会做的第一件事情是将传递给 setState 的对象合并到组件的当前状态
这将启动一个称为和解(
reconciliation
)的过程。和解(reconciliation
)的最终目标是以最有效的方式,根据这个新的状态来更新UI
。 为此,React
将构建一个新的React
元素树(您可以将其视为UI
的对象表示)一旦有了这个树,为了弄清 UI 如何响应新的状态而改变,React 会将这个新树与上一个元素树相比较( diff )
通过这样做, React 将会知道发生的确切变化,并且通过了解发生什么变化,只需在绝对必要的情况下进行更新即可最小化 UI 的占用空间
React 如何区分 Class 组件 和 Function 组件
一般的方式是借助 typeof 和 Function.prototype.toString 来判断当前是不是 class,如下:
但是这个方式有它的局限性,因为如果用了 babel 等转换工具,将 class 写法全部转为 function 写法,上面的判断就会失效。
React 区分 Class 组件 和 Function 组件的方式很巧妙,由于所有的类组件都要继承 React.Component,所以只要判断原型链上是否有 React.Component 就可以了:
父子组件的通信方式?
父组件向子组件通信:父组件通过 props 向子组件传递需要的信息。
子组件向父组件通信:: props+回调的方式。
如何配置 React-Router 实现路由切换
(1)使用<Route>
组件
路由匹配是通过比较 <Route>
的 path 属性和当前地址的 pathname 来实现的。当一个 <Route>
匹配成功时,它将渲染其内容,当它不匹配时就会渲染 null。没有路径的 <Route>
将始终被匹配。
(2)结合使用 <Switch>
组件和 <Route>
组件
<Switch>
用于将 <Route>
分组。
<Switch>
不是分组 <Route>
所必须的,但他通常很有用。 一个 <Switch>
会遍历其所有的子 <Route>
元素,并仅渲染与当前地址匹配的第一个元素。
(3)使用 <Link>、 <NavLink>、<Redirect>
组件
<Link>
组件来在你的应用程序中创建链接。无论你在何处渲染一个<Link>
,都会在应用程序的 HTML 中渲染锚(<a>
)。
是一种特殊类型的 当它的 to 属性与当前地址匹配时,可以将其定义为"活跃的"。
当我们想强制导航时,可以渲染一个<Redirect>
,当一个<Redirect>
渲染时,它将使用它的 to 属性进行定向。
如何创建 refs
Refs 是使用 React.createRef()
创建的,并通过 ref
属性附加到 React 元素。在构造组件时,通常将 Refs
分配给实例属性,以便可以在整个组件中引用它们。
或者这样用:
为什么 useState 要使用数组而不是对象
useState 的用法:
可以看到 useState 返回的是一个数组,那么为什么是返回数组而不是返回对象呢?
这里用到了解构赋值,所以先来看一下 ES6 的解构赋值:
数组的解构赋值
对象的解构赋值
看完这两个例子,答案应该就出来了:
如果 useState 返回的是数组,那么使用者可以对数组中的元素命名,代码看起来也比较干净
如果 useState 返回的是对象,在解构对象的时候必须要和 useState 内部实现返回的对象同名,想要使用多次的话,必须得设置别名才能使用返回值
下面来看看如果 useState 返回对象的情况:
这里可以看到,返回对象的使用方式还是挺麻烦的,更何况实际项目中会使用的更频繁。 总结:useState 返回的是 array 而不是 object 的原因就是为了降低使用的复杂度,返回数组的话可以直接根据顺序解构,而返回对象的话要想使用多次就需要定义别名了。
React Portal 有哪些使用场景
在以前, react 中所有的组件都会位于 #app 下,而使用 Portals 提供了一种脱离 #app 的组件
因此 Portals 适合脱离文档流(out of flow) 的组件,特别是 position: absolute 与 position: fixed 的组件。比如模态框,通知,警告,goTop 等。
以下是官方一个模态框的示例,可以在以下地址中测试效果
React Hooks 当中的 useEffect 是如何区分生命周期钩子的
useEffect 可以看成是
componentDidMount
,componentDidUpdate
和componentWillUnmount
三者的结合。useEffect(callback, [source])接收两个参数,调用方式如下
生命周期函数的调用主要是通过第二个参数[source]来进行控制,有如下几种情况:
[source]
参数不传时,则每次都会优先调用上次保存的函数中返回的那个函数,然后再调用外部那个函数;[source]
参数传[]时,则外部的函数只会在初始化时调用一次,返回的那个函数也只会最终在组件卸载时调用一次;[source]
参数有值时,则只会监听到数组中的值发生变化后才优先调用返回的那个函数,再调用外部的函数。
评论