写点什么

写给自己的 react 面试题总结

作者:beifeng1996
  • 2022 年 9 月 22 日
    浙江
  • 本文字数:6180 字

    阅读完需:约 20 分钟

解释 React 中 render() 的目的。

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


ssr 原理是什么?

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

React 组件命名推荐的方式是哪个?

通过引用而不是使用来命名组件 displayName。


使用 displayName 命名组件:


export default React.createClass({  displayName: 'TodoApp',  // ...})
复制代码


前端react面试题详细解答


React 推荐的方法:


export default class TodoApp extends React.Component {  // ...}
复制代码

React 必须使用 JSX 吗?

React 并不强制要求使用 JSX。当不想在构建环境中配置有关 JSX 编译时,不在 React 中使用 JSX 会更加方便。


每个 JSX 元素只是调用 React.createElement(component, props, ...children) 的语法糖。因此,使用 JSX 可以完成的任何事情都可以通过纯 JavaScript 完成。


例如,用 JSX 编写的代码:


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


可以编写为不使用 JSX 的代码:


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'));
复制代码

Redux Thunk 的作用是什么

Redux thunk 是一个允许你编写返回一个函数而不是一个 action 的 actions creators 的中间件。如果满足某个条件,thunk 则可以用来延迟 action 的派发(dispatch),这可以处理异步 action 的派发(dispatch)。

为什么使用 jsx 的组件中没有看到使用 react 却需要引入 react?

本质上来说 JSX 是React.createElement(component, props, ...children)方法的语法糖。在 React 17 之前,如果使用了 JSX,其实就是在使用 React, babel 会把组件转换为 CreateElement 形式。在 React 17 之后,就不再需要引入,因为 babel 已经可以帮我们自动引入 react。

React 中 props.children 和 React.Children 的区别

在 React 中,当涉及组件嵌套,在父组件中使用props.children把所有子组件显示出来。如下:


function ParentComponent(props){    return (        <div>            {props.children}        </div>    )}
复制代码


如果想把父组件中的属性传给所有的子组件,需要使用React.Children方法。


比如,把几个 Radio 组合起来,合成一个 RadioGroup,这就要求所有的 Radio 具有同样的 name 属性值。可以这样:把 Radio 看做子组件,RadioGroup 看做父组件,name 的属性值在 RadioGroup 这个父组件中设置。


首先是子组件:


//子组件function RadioOption(props) {  return (    <label>      <input type="radio" value={props.value} name={props.name} />      {props.label}    </label>  )}
复制代码


然后是父组件,不仅需要把它所有的子组件显示出来,还需要为每个子组件赋上 name 属性和值:


//父组件用,props是指父组件的propsfunction renderChildren(props) {
//遍历所有子组件 return React.Children.map(props.children, child => { if (child.type === RadioOption) return React.cloneElement(child, { //把父组件的props.name赋值给每个子组件 name: props.name }) else return child })}//父组件function RadioGroup(props) { return ( <div> {renderChildren(props)} </div> )}function App() { return ( <RadioGroup name="hello"> <RadioOption label="选项一" value="1" /> <RadioOption label="选项二" value="2" /> <RadioOption label="选项三" value="3" /> </RadioGroup> )}export default App;
复制代码


以上,React.Children.map让我们对父组件的所有子组件又更灵活的控制。

react 生命周期

初始化阶段:


  • getDefaultProps:获取实例的默认属性

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

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

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

  • componentDidMount:组件真正在被装载之后


运行中状态:


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

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

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

  • render:组件重新描绘

  • componentDidUpdate:组件已经更新


销毁阶段:


  • componentWillUnmount:组件即将销毁


shouldComponentUpdate 是做什么的,(react 性能优化是哪个周期函数?)


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


在 react17 会删除以下三个生命周期


componentWillMount,componentWillReceiveProps , componentWillUpdate

useEffect 和 useLayoutEffect 的区别

useEffect


基本上 90%的情况下,都应该用这个,这个是在 render 结束后,你的 callback 函数执行,但是不会 block browser painting,算是某种异步的方式吧,但是 class 的 componentDidMount 和 componentDidUpdate 是同步的,在 render 结束后就运行,useEffect 在大部分场景下都比 class 的方式性能更好.


useLayoutEffect


这个是用在处理 DOM 的时候,当你的 useEffect 里面的操作需要处理 DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect 里面的 callback 函数会在**DOM 更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,**阻塞了浏览器的绘制.

何为 redux

Redux 的基本思想是整个应用的 state 保持在一个单一的 store 中。store 就是一个简单的 javascript 对象,而改变应用 state 的唯一方式是在应用中触发 actions,然后为这些 actions 编写 reducers 来修改 state。整个 state 转化是在 reducers 中完成,并且不应该有任何副作用。

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,并清空这个队列,然后渲染组件。

(组件的)状态(state)和属性(props)之间有何不同

State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。


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

对 React SSR 的理解

服务端渲染是数据与模版组成的 html,即 HTML = 数据 + 模版。将组件或页面通过服务器生成 html 字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序。页面没使用服务渲染,当请求页面时,返回的 body 里为空,之后执行 js 将 html 结构注入到 body 里,结合 css 显示出来;


SSR 的优势:


  • 对 SEO 友好

  • 所有的模版、图片等资源都存在服务器端

  • 一个 html 返回所有数据

  • 减少 HTTP 请求

  • 响应快、用户体验好、首屏渲染快


1)更利于 SEO


不同爬虫工作原理类似,只会爬取源码,不会执行网站的任何脚本使用了 React 或者其它 MVVM 框架之后,页面大多数 DOM 元素都是在客户端根据 js 动态生成,可供爬虫抓取分析的内容大大减少。另外,浏览器爬虫不会等待我们的数据完成之后再去抓取页面数据。服务端渲染返回给客户端的是已经获取了异步数据并执行 JavaScript 脚本的最终 HTML,网络爬中就可以抓取到完整页面的信息。


2)更利于首屏渲染


首屏的渲染是 node 发送过来的 html 字符串,并不依赖于 js 文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会有一个很长的白屏等待时间。


SSR 的局限:


1)服务端压力较大


本来是通过客户端完成渲染,现在统一到服务端 node 服务去做。尤其是高并发访问的情况,会大量占用服务端 CPU 资源;


2)开发条件受限


在服务端渲染中,只会执行到 componentDidMount 之前的生命周期钩子,因此项目引用的第三方的库也不可用其它生命周期钩子,这对引用库的选择产生了很大的限制;


3)学习成本相对较高 除了对 webpack、MVVM 框架要熟悉,还需要掌握 node、 Koa2 等相关技术。相对于客户端渲染,项目构建、部署过程更加复杂。


时间耗时比较:


1)数据请求


由服务端请求首屏数据,而不是客户端请求首屏数据,这是"快"的一个主要原因。服务端在内网进行请求,数据响应速度快。客户端在不同网络环境进行数据请求,且外网 http 请求开销大,导致时间差


  • 客户端数据请求

  • 服务端数据请求

  • 2)html 渲染 服务端渲染是先向后端服务器请求数据,然后生成完整首屏 html 返回给浏览器;而客户端渲染是等 js 代码下载、加载、解析完成后再请求数据渲染,等待的过程页面是什么都没有的,就是用户看到的白屏。就是服务端渲染不需要等待 js 代码下载完成并请求数据,就可以返回一个已有完整数据的首屏页面。

  • 非 ssr html 渲染

  • ssr html 渲染

HOC 相比 mixins 有什么优点?

HOC 和 Vue 中的 mixins 作用是一致的,并且在早期 React 也是使用 mixins 的方式。但是在使用 class 的方式创建组件以后,mixins 的方式就不能使用了,并且其实 mixins 也是存在一些问题的,比如:


  • 隐含了一些依赖,比如我在组件中写了某个 state 并且在 mixin 中使用了,就这存在了一个依赖关系。万一下次别人要移除它,就得去 mixin 中查找依赖

  • 多个 mixin 中可能存在相同命名的函数,同时代码组件中也不能出现相同命名的函数,否则就是重写了,其实我一直觉得命名真的是一件麻烦事。。

  • 雪球效应,虽然我一个组件还是使用着同一个 mixin,但是一个 mixin 会被多个组件使用,可能会存在需求使得 mixin 修改原本的函数或者新增更多的函数,这样可能就会产生一个维护成本


HOC 解决了这些问题,并且它们达成的效果也是一致的,同时也更加的政治正确(毕竟更加函数式了)。

为什么建议传递给 setState 的参数是一个 callback 而不是一个对象

因为 this.propsthis.state 的更新可能是异步的,不能依赖它们的值去计算下一个 state。

当调用 setState 的时候,发生了什么操作?**

当调用 setState 时, React 做的第一件事是将传递给 setState 的对象合并到组件的当前状态,这将启动一个称为和解( reconciliation)的过程。和解的最终目标是,根据这个新的状态以最有效的方式更新 DOM。为此, React 将构建一个新的 React 虚拟 DOM 树(可以将其视为页面 DOM 元素的对象表示方式)。一旦有了这个 DOM 树,为了弄清 DOM 是如何响应新的状态而改变的, React 会将这个新树与上一个虚拟 DOM 树比较。这样做, React 会知道发生的确切变化,并且通过了解发生的变化后,在绝对必要的情况下进行更新 DOM,即可将因操作 DOM 而占用的空间最小化。

React 16 中新生命周期有哪些

关于 React16 开始应用的新生命周期: 可以看出,React16 自上而下地对生命周期做了另一种维度的解读:


  • Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(我们后面会重点讲解)是有关的;

  • Pre-commit 阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新真实的 DOM,不过 DOM 信息已经是可以读取的了;

  • Commit 阶段:在这一步,React 会完成真实 DOM 的更新工作。Commit 阶段,我们可以拿到真实 DOM(包括 refs)。


与此同时,新的生命周期在流程方面,仍然遵循“挂载”、“更新”、“卸载”这三个广义的划分方式。它们分别对应到:


  • 挂载过程:

  • constructor

  • getDerivedStateFromProps

  • render

  • componentDidMount

  • 更新过程:

  • getDerivedStateFromProps

  • shouldComponentUpdate

  • render

  • getSnapshotBeforeUpdate

  • componentDidUpdate

  • 卸载过程:

  • componentWillUnmount

React 中 refs 的作用是什么?有哪些应用场景?

Refs 提供了一种方式,用于访问在 render 方法中创建的 React 元素或 DOM 节点。Refs 应该谨慎使用,如下场景使用 Refs 比较适合:


  • 处理焦点、文本选择或者媒体的控制

  • 触发必要的动画

  • 集成第三方 DOM 库


Refs 是使用 React.createRef() 方法创建的,他通过 ref 属性附加到 React 元素上。


要在整个组件中使用 Refs,需要将 ref 在构造函数中分配给其实例属性:


class MyComponent extends React.Component {  constructor(props) {    super(props)    this.myRef = React.createRef()  }  render() {    return <div ref={this.myRef} />  }}
复制代码

react 和 vue 的共同点和区别

相同点


  • 两者都是用了虚拟 dom

  • 都鼓励使用组件化应用

  • 都可以通过 cli 快速创建项目,也都有自己的状态管理工具

  • 支持数据驱动试图

  • 都支持服务端渲染


不同点


  • 设计思想不同

  • react 是函数式思想,使用 jsx 语法,all in js

  • vue 是响应式思想,也是基于数据可变的,把 html css js 组合到一起

  • 渲染方式不同

  • react 默认状态改变时会重新渲染所有子组件(当然也可以在 shouldCompoentUpdate 生命周期中控制不更新)

  • vue 在渲染过程中会跟踪每一个组件的依赖关系,不需要渲染整个组件树

  • 性能不同

  • react 适合大中型项目

  • vue 使用中小型项目

hooks 父子传值

父传子在父组件中用useState声明数据 const [ data, setData ] = useState(false)
把数据传递给子组件<Child data={data} />
子组件接收export default function (props) { const { data } = props console.log(data)}子传父子传父可以通过事件方法传值,和父传子有点类似。在父组件中用useState声明数据 const [ data, setData ] = useState(false)
把更新数据的函数传递给子组件<Child setData={setData} />
子组件中触发函数更新数据,就会直接传递给父组件export default function (props) { const { setData } = props setData(true)}如果存在多个层级的数据传递,也可依照此方法依次传递
// 多层级用useContextconst User = () => { // 直接获取,不用回调 const { user, setUser } = useContext(UserContext); return <Avatar user={user} setUser={setUser} />;};
复制代码


用户头像

beifeng1996

关注

还未添加个人签名 2022.09.01 加入

还未添加个人简介

评论

发布
暂无评论
写给自己的react面试题总结_前端_beifeng1996_InfoQ写作社区