滴滴前端常考react面试题(附答案)

2022-09-13 14:08:53 浏览数 (1)

Hooks可以取代 render props 和高阶组件吗?

通常,render props和高阶组件仅渲染一个子组件。React团队认为,Hooks 是服务此用例的更简单方法。

这两种模式仍然有一席之地(例如,一个虚拟的 scroller 组件可能有一个 renderItem prop,或者一个可视化的容器组件可能有它自己的 DOM 结构)。但在大多数情况下,Hooks 就足够了,可以帮助减少树中的嵌套。

ssr原理是什么?

核心原理其实就是借助虚拟DOM来实现react代码能够在服务器运行的,node里面可以执行react代码

在 React中元素( element)和组件( component)有什么区别?

简单地说,在 React中元素(虛拟DOM)描述了你在屏幕上看到的DOM元素。

换个说法就是,在 React中元素是页面中DOM元素的对象表示方式。在 React中组件是一个函数或一个类,它可以接受输入并返回一个元素。

注意:工作中,为了提高开发效率,通常使用JSX语法表示 React元素(虚拟DOM)。在编译的时候,把它转化成一个 React. createElement调用方法。

解释 React 中 render() 的目的。

每个React组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,则必须将它们组合在一个封闭标记内,例如 <form><group><div> 等。此函数必须保持纯净,即必须每次调用时都返回相同的结果。

为什么 React 要用 JSX?

JSX 是一个 JavaScript 的语法扩展,或者说是一个类似于 XML 的 ECMAScript 语法扩展。它本身没有太多的语法定义,也不期望引入更多的标准。

其实 React 本身并不强制使用 JSX。在没有 JSX 的时候,React 实现一个组件依赖于使用 React.createElement 函数。代码如下:

代码语言:javascript复制
class Hello extends React.Component {
  render() {
    return React.createElement(
        'div',
        null, 
        `Hello ${this.props.toWhat}`
      );
  }
}
ReactDOM.render(
  React.createElement(Hello, {toWhat: 'World'}, null),
  document.getElementById('root')
);

而 JSX 更像是一种语法糖,通过类似 XML 的描述方式,描写函数对象。在采用 JSX 之后,这段代码会这样写:

代码语言:javascript复制
class Hello extends React.Component {
  render() {
    return <div>Hello {this.props.toWhat}</div>;
  }
}
ReactDOM.render(
  <Hello toWhat="World" />,
  document.getElementById('root')
);

通过对比,可以清晰地发现,代码变得更为简洁,而且代码结构层次更为清晰。

因为 React 需要将组件转化为虚拟 DOM 树,所以在编写代码时,实际上是在手写一棵结构树。而XML 在树结构的描述上天生具有可读性强的优势。

但这样可读性强的代码仅仅是给写程序的同学看的,实际上在运行的时候,会使用 Babel 插件将 JSX 语法的代码还原为 React.createElement 的代码。

总结: JSX 是一个 JavaScript 的语法扩展,结构类似 XML。JSX 主要用于声明 React 元素,但 React 中并不强制使用 JSX。即使使用了 JSX,也会在构建过程中,通过 Babel 插件编译为 React.createElement。所以 JSX 更像是 React.createElement 的一种语法糖。

React 团队并不想引入 JavaScript 本身以外的开发体系。而是希望通过合理的关注点分离保持组件开发的纯粹性。

Redux 中间件是怎么拿到store 和 action? 然后怎么处理?

redux中间件本质就是一个函数柯里化。redux applyMiddleware Api 源码中每个middleware 接受2个参数, Store 的getState 函数和dispatch 函数,分别获得store和action,最终返回一个函数。该函数会被传入 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store的 dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是({ getState,dispatch })=> next => action。

何为 reducer

一个 reducer 是一个纯函数,该函数以先前的 state 和一个 action 作为参数,并返回下一个 state。

区分状态和 props

条件

State

Props

  1. 从父组件中接收初始值

Yes

Yes

  1. 父组件可以改变值

No

Yes

  1. 在组件中设置默认值

Yes

Yes

  1. 在组件的内部变化

Yes

No

  1. 设置子组件的初始值

Yes

Yes

  1. 在子组件的内部更改

No

Yes

React中可以在render访问refs吗?为什么?

代码语言:javascript复制
<>
  <span id="name" ref={this.spanRef}>{this.state.title}</span>
  <span>{     this.spanRef.current ? '有值' : '无值'  }</span>
</>

不可以,render 阶段 DOM 还没有生成,无法获取 DOM。DOM 的获取需要在 pre-commit 阶段和 commit 阶段:

React 中的key是什么?为什么它们很重要?

key可以帮助 React跟踪循环创建列表中的虚拟DOM元素,了解哪些元素已更改、添加或删除。

每个绑定key的虚拟DOM元素,在兄弟元素之间都是独一无二的。在 React的和解过程中,比较新的虛拟DOM树与上一个虛拟DOM树之间的差异,并映射到页面中。key使 React处理列表中虛拟DOM时更加高效,因为 React可以使用虛拟DOM上的key属性,快速了解元素是新的、需要删除的,还是修改过的。如果没有key,Rεat就不知道列表中虚拟DOM元素与页面中的哪个元素相对应。所以在创建列表的时候,不要忽略key。

在 ReactNative中,如何解决 adb devices找不到连接设备的问题?

在使用 Genymotion时,首先需要在SDK的 platform-tools中加入环境变量,然后在 Genymotion中单击 Setting,选择ADB选项卡,单击 Use custom Android SDK tools,浏览本地SDK的位置,单击OK按钮就可以了。启动虛拟机后,在cmd中输入 adb devices可以查看设备。

如何配置 React-Router 实现路由切换

(1)使用<Route> 组件

路由匹配是通过比较 <Route> 的 path 属性和当前地址的 pathname 来实现的。当一个 <Route> 匹配成功时,它将渲染其内容,当它不匹配时就会渲染 null。没有路径的 <Route> 将始终被匹配。

代码语言:javascript复制
// when location = { pathname: '/about' }
<Route path='/about' component={About}/> // renders <About/>
<Route path='/contact' component={Contact}/> // renders null
<Route component={Always}/> // renders <Always/>

(2)结合使用 <Switch> 组件和 <Route> 组件

<Switch> 用于将 <Route> 分组。

代码语言:javascript复制
<Switch>
    <Route exact path="/" component={Home} />
    <Route path="/about" component={About} />
    <Route path="/contact" component={Contact} />
</Switch>

<Switch> 不是分组 <Route> 所必须的,但他通常很有用。 一个 <Switch> 会遍历其所有的子 <Route>元素,并仅渲染与当前地址匹配的第一个元素。

(3)使用 <Link>、 <NavLink>、<Redirect> 组件

<Link> 组件来在你的应用程序中创建链接。无论你在何处渲染一个<Link> ,都会在应用程序的 HTML 中渲染锚(<a>)。

代码语言:javascript复制
<Link to="/">Home</Link>   
// <a href='/'>Home</a>

是一种特殊类型的 当它的 to属性与当前地址匹配时,可以将其定义为"活跃的"。

代码语言:javascript复制
// location = { pathname: '/react' }
<NavLink to="/react" activeClassName="hurray">
    React
</NavLink>
// <a href='/react' className='hurray'>React</a>

当我们想强制导航时,可以渲染一个<Redirect>,当一个<Redirect>渲染时,它将使用它的to属性进行定向。

Redux 状态管理器和变量挂载到 window 中有什么区别

两者都是存储数据以供后期使用。但是Redux状态更改可回溯——Time travel,数据多了的时候可以很清晰的知道改动在哪里发生,完整的提供了一套状态管理模式。

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。

如果这还不够糟糕,考虑一些来自前端开发领域的新需求,如更新调优、服务端渲染、路由跳转前请求数据等等。前端开发者正在经受前所未有的复杂性,难道就这么放弃了吗?当然不是。

这里的复杂性很大程度上来自于:我们总是将两个难以理清的概念混淆在一起:变化和异步。 可以称它们为曼妥思和可乐。如果把二者分开,能做的很好,但混到一起,就变得一团糟。一些库如 React 视图在视图层禁止异步和直接操作 DOM来解决这个问题。美中不足的是,React 依旧把处理 state 中数据的问题留给了你。Redux就是为了帮你解决这个问题。

setState 是同步异步?为什么?实现原理?

1. setState是同步执行的

setState是同步执行的,但是state并不一定会同步更新

2. setState在React生命周期和合成事件中批量覆盖执行

在React的生命周期钩子和合成事件中,多次执行setState,会批量执行

具体表现为,多次同步执行的setState,会进行合并,类似于Object.assign,相同的key,后面的会覆盖前面的

当遇到多个setState调用时候,会提取单次传递setState的对象,把他们合并在一起形成一个新的

单一对象,并用这个单一的对象去做setState的事情,就像Object.assign的对象合并,后一个

key值会覆盖前面的key值

经过React 处理的事件是不会同步更新 this.state的. 通过 addEventListener || setTimeout/setInterval 的方式处理的则会同步更新。

为了合并setState,我们需要一个队列来保存每次setState的数据,然后在一段时间后执行合并操作和更新state,并清空这个队列,然后渲染组件。

react-redux 的实现原理?

通过 redux 和 react context 配合使用,并借助高阶函数,实现了 react-redux

在 React 中,refs 的作用是什么

Refs 可以用于获取一个 DOM 节点或者 React 组件的引用。何时使用 refs 的好的示例有管理焦点/文本选择,触发命令动画,或者和第三方 DOM 库集成。你应该避免使用 String 类型的 Refs 和内联的 ref 回调。Refs 回调是 React 所推荐的。

在React中怎么使用async/await?

async/await是ES7标准中的新特性。如果是使用React官方的脚手架创建的项目,就可以直接使用。如果是在自己搭建的webpack配置的项目中使用,可能会遇到 regeneratorRuntime is not defined 的异常错误。那么我们就需要引入babel,并在babel中配置使用async/await。可以利用babel的 transform-async-to-module-method 插件来转换其成为浏览器支持的语法,虽然没有性能的提升,但对于代码编写体验要更好。

react16版本的reconciliation阶段和commit阶段是什么

  • reconciliation阶段包含的主要工作是对current tree 和 new tree 做diff计算,找出变化部分。进行遍历、对比等是可以中断,歇一会儿接着再来。
  • commit阶段是对上一阶段获取到的变化部分应用到真实的DOM树中,是一系列的DOM操作。不仅要维护更复杂的DOM状态,而且中断后再继续,会对用户体验造成影响。在普遍的应用场景下,此阶段的耗时比diff计算等耗时相对短。

React Portal 有哪些使用场景

  • 在以前, react 中所有的组件都会位于 #app 下,而使用 Portals 提供了一种脱离 #app 的组件
  • 因此 Portals 适合脱离文档流(out of flow) 的组件,特别是 position: absolute 与 position: fixed的组件。比如模态框,通知,警告,goTop 等。

以下是官方一个模态框的示例,可以在以下地址中测试效果

代码语言:html复制
<html>
  <body>
    <div id="app"></div>
    <div id="modal"></div>
    <div id="gotop"></div>
    <div id="alert"></div>
  </body>
</html>
代码语言:javascript复制
const modalRoot = document.getElementById('modal');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el,
    );
  }
}

React Hooks当中的useEffect是如何区分生命周期钩子的

useEffect可以看成是componentDidMountcomponentDidUpdatecomponentWillUnmount三者的结合。useEffect(callback, source)接收两个参数,调用方式如下

代码语言:javascript复制
useEffect(() => {
   console.log('mounted');

   return () => {
       console.log('willUnmount');
   }
 }, [source]);

生命周期函数的调用主要是通过第二个参数source来进行控制,有如下几种情况:

  • [source]参数不传时,则每次都会优先调用上次保存的函数中返回的那个函数,然后再调用外部那个函数;
  • [source]参数传[]时,则外部的函数只会在初始化时调用一次,返回的那个函数也只会最终在组件卸载时调用一次;
  • [source]参数有值时,则只会监听到数组中的值发生变化后才优先调用返回的那个函数,再调用外部的函数。

可以使用TypeScript写React应用吗?怎么操作?

(1)如果还未创建 Create React App 项目

  • 直接创建一个具有 typescript 的 Create React App 项目:
代码语言:javascript复制
 npx create-react-app demo --typescript

(2)如果已经创建了 Create React App 项目,需要将 typescript 引入到已有项目中

  • 通过命令将 typescript 引入项目:
代码语言:javascript复制
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
  • 将项目中任何 后缀名为 ‘.js’ 的 JavaScript 文件重命名为 TypeScript 文件即后缀名为 ‘.tsx’(例如 src/index.js 重命名为 src/index.tsx )

0 人点赞