React 面试题

1、组件通信的方式

父组件传子组件:通过props 的方式
子组件传父组件:父组件将自身函数传入,子组件调用该函数,父组件在函数中拿到子组件传递的数据
兄弟组件通信:找到共同的父节点,用父节点转发进行通信
跨层级通信:使用contetx 可跨域多层传递全局数据
全局状态管理工具:redux 维护一个全局状态中心的store

2、react 的生命周期

挂载阶段:

constructor():constructor中通常只做两件事:初始化组件的 state、给事件处理方法绑定 this
static getDerivedStateFromProps():该函数会在创建或更新props或state时调用
render():用于渲染DOM结构
componentDidMount():在组件挂载后(插入 DOM 树中)立即调,该阶段可执行依赖于DOM的操作、发送网络请求;(官方建议)

更新阶段:

static getDerivedStateFromProps()
shouldComponentUpdate():建议使用该方法会产生明显的性能提升时使用
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

卸载阶段:

componentWillUnmount():在组件卸载及销毁之前直接调用,该阶段可清除 timer,取消网络请求或清除

错误处理:

static getDerivedStateFromError()
componentDidCatch():此生命周期在后代组件抛出错误后被调用

3、 props 改变后在哪个生命周期中处理

getDerivedStateFromProps生命周期会在组件接收新的 props 时调用;这个生命周期函数是为了替代componentWillReceiveProps存在

4、 React 性能优化在哪个生命周期?

shouldComponentUpdate
react的父级组件的render函数重新渲染会引起子组件的render方法的重新渲染。但是,有的时候子组件的接受父组件的数据没有变动,子组件render的执行会影响性能

shouldComponentUpdate(nexrProps) {
    if (this.props.num === nexrProps.num) {
        return false
    }
    return true;
}

shouldComponentUpdate提供了两个参数nextProps和nextState,表示下一次props和一次state的值,当函数返回false时候,render()方法不执行,组件也就不会渲染,返回true时,组件照常重渲染。

5、网络请求在那个生命周期中进行

一般在在componentDidMount中去操作

6、setState 是同步的还是异步的

默认情况下是异步的,比如在 React 生命周期事件和合成事件中,都会走合并操作,延迟更新的策略。但是在 React 无法控制的地方,比如原生事件,具体就是在 addEventListener 、setTimeout、setInterval 等事件中,就只能同步更新。这个问题根本原因就是 React 在自己管理的事件回调和生命周期中,对于 setState 是批量更新的,而在其他时候是立即更新的。
setState 的异步设计是为了性能优化、减少渲染次数,假如所有setState是同步的,意味着每执行一次setState时(有可能一个同步代码中,多次setState),都重新vnode diff + dom修改,界面重新渲染,这对性能来说是极为不好的。如果是异步,则可以把一个同步代码中的多个setState合并成一次组件更新。

7、 react 移除了哪些生命周期方法

componentWillMount() 、componentWillUpdate()、componentWillReceiveProps()、

8、 React组件的state和props有什么区别

1、props 是传递给组件的(类似于函数的形参),而state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)
2、props 是不可修改的,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改,state 是多变的、可以修改,每次setState都异步更新的。

9、React中的props为什么是只读的

React中的props是只读的,主要是为了维护组件的可预测性和可维护性。
如果允许子组件修改props,那么一个父组件将状态传递给多个子组件时,这些子组件就可以随意修改props的值,导致状态的不可预测,给调试和维护带来困难。为了解决这个问题,React采用了只读的props机制。子组件只能读取props的值,但不能修改它们。这种设计模式类似于函数式编程中的纯函数,纯函数不会改变传入的参数,只会返回一个新的值。通过保护props不被修改,React确保了组件的可预测性和可维护性

10、说说你对react的理解

React是一种用于构建用户界面的JavaScript库,它的全称是React.js。它由Facebook开发,旨在帮助开发者更高效地构建大型、高性能的Web应用程序。React通过使用虚拟DOM(Document Object Model)来实现高效的页面更新,使得开发者可以通过简单的组件化思想来构建复杂的用户界面。

11、react有什么特点

1、 声明式设计:React采用声明式设计范式,可以轻松描述应用程序的状态,而不必关注具体实现细节,这让开发者更专注于业务逻辑。
2、高效:React通过对虚拟DOM的模拟,最大限度地减少与DOM的交互,提高了应用程序的性能和响应速度。
3、组件化:React采用组件化的开发模式,使开发者可以将应用程序拆分成小的、独立的组件,方便重复利用和维护。
4、单向数据流:React采用单向数据流的数据绑定方式,使得数据的流动清晰可见,方便开发者追踪和调试。
5、生态系统:React有一个强大的生态系统,包括React Router、Redux等,可以轻松实现路由管理、状态管理等。

12、组件化有什么好处

组件是独立和可复用的代码组织单元,它使开发者使用小型、独立和通常可复用的组件构建大型应用;
好处:
1、调试方便,根据报错的组件快速定位问题
2、提高可维护性,由于每个组件的职责单一
3、提高应用开发效率,快速搭建页面

13、声明式和命令式的区别

编程范式不同:声明式编程关注的是要做什么,而不是如何做;命令式编程关注的是如何做
代码执行方式不同:声明式代码指示JavaScript如何执行每个步骤;命令式代码每行命令指示程序如何进行下一步详细操作
工作方式不同:声明式编程中,开发人员只描述他们想要实现的目标,而无需列出使其工作的所有步骤;命令式编程中,开发人员需要详细列出每一步的工作

14、怎么理解react的声明式设计

React的声明式设计是指开发者只需要关注UI的状态和数据,而不需要关注具体的UI更新逻辑。通过定义组件的状态来描述UI的外观和行为,当状态发生变化时,React会根据状态的变化自动更新UI

15、react的优点和缺点

优点:
1、提高开发效率:React的声明式设计和组件化开发模式可以提高开发效率,减少代码量和开发时间。
2、高性能:React采用虚拟DOM和优化的渲染机制,提高了应用程序的性能和响应速度。
3、跨平台:React可以使用React Native开发原生应用程序,实现跨平台开发。
4、跨浏览器兼容:虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。
缺点:
1、React 只是 视图层的一个框架,如果需要做其他事情,需要依赖它的生态系统;如处理单页面路由使用 Router,处理数据使用 Redux。

16、什么是虚拟DOM

虚拟 DOM(Virtual DOM)本质上是JS 和 DOM 之间的一个映射缓存,它在形态上表现为一个能够描述 DOM 结构及其属性信息的 JS 对象。它主要存储在内存中,当数据变化的时候,生成新的DOM,对比新旧虚拟DOM的差异,将差异更新到真实DOM上

17、为什么使用虚拟DOM可以提高性能

当每一次UI更新时,总会根据render重新生成最新的VNode,然后跟以前缓存起来老的VNode进行比对,再使用Diff算法(框架核心)去真正更新真实DOM(虚拟DOM是JS对象结构,同样在JS引擎中,而真实DOM在浏览器渲染引擎中,所以操作虚拟DOM比操作真实DOM开销要小的多)

18、受控组件和非受控组件

1、受控组件的状态由React组件的state来控制。在受控组件中,表单元素(如输入框、选择框等)的值通过受控组件的状态进行同步
2、非受控组件的状态不由React组件的state控制。在非受控组件中,表单元素的值由用户的交互直接更新,而不会通过React组件的状态进行同步,如使用ref
大部分时候使用受控组件来处理表单数据和状态

19、react 和 vue 的区别

1、react 是由Facebook开发的,Vue是由尤雨溪开发的
2、在组件化的差异:React推荐的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都写进 JavaScript 中,即 all in js;
Vue 推荐的做法是 template 的单文件组件格式(简单易懂,从传统前端转过来易于理解),即 html,css,JS 写在同一个文件(vue也支持JSX写法)
3、dom的更新策略不同:
react 会自顶向下全diff。vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
在react中,当状态发生改变时,组件树就会自顶向下的全diff, 重新render页面, 重新生成新的虚拟dom tree, 新旧dom tree进行比较, 进行patch打补丁方式,局部更新dom。所以react为了避免父组件更新而引起不必要的子组件更新, 可以在shouldComponentUpdate做逻辑判断,减少没必要的render, 以及重新生成虚拟dom,做差量对比过程。
在vue中, 通过Object.defineProperty 把 data 属性全部转为 getter/setter,追踪每个组件的依赖关系,同时watcher实例对象会在组件渲染时,将属性记录为dep, 当dep 项中的 setter被调用时,通知watch重新计算,使得关联组件更新。
Diff 算法借助元素的 Key 判断元素是新增、删除、修改,从而减少不必要的元素重渲染。
4、框架本质不同
Vue本质是MVVM框架,由MVC发展而来;React是前端组件化框架,由后端组件化发展而来。
5、react 没有双向数据绑定,就父子组件的数据流动来说,都是单项的,子组件不能直接修改父组件的prop,但是vue 有v-modle 监听视图的修改更新数据,而react中通常需要手动调用setState 方法来更新状态

20、react 和 vue 的共同点

他们都是用于构建用户界面的JavaScript库,提供了一套UI解决方案,都有声明式和组件化开发的特点,在渲染上都使用了虚拟DOM进行优化,并且都支持数据驱动,通常开发者无需手动操作DOM,他们也都支持服务端渲染都有native方案…
1、都有组件化开发和虚拟DOM
2、都支持数据驱动,不需要直接操作DOM
3、都支持服务端渲染
4、都有native的方案,react native weex
5、都将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库

21、React 高阶组件、Render props、hooks 有什么区别

高阶组件
(High Order Component,简称HOC)它是一种基于 React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回一个功能增强的新组件的函数。
优点:高阶组件通常可以用来逻辑复用、不影响被包裹组件的内部逻辑,比如权限控制、数据校验、异常处理。
缺点∶固定的 props 可能会因为重名而被覆盖(如果被包裹的组件和高阶组件有同名props)

// HOC.js
function withSubscription(WrappedComponent, selectData) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        data: selectData(DataSource, props)
      };
    }
    // 一些通用的逻辑处理
    render() {
      // ... 并使用新数据渲染被包装的组件!
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

render prop
"render prop"是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术。
具有render prop 的组件接受一个返回React元素的函数,将render的渲染逻辑注入到组件内部。在这里,"render"的命名可以是任何其他有效的标识符。
功能:将一个组件内的 state 作为 props 传递给调用者, 调用者可以动态的决定如何渲染.

// DataProvider组件内部的渲染逻辑如下
class DataProvider extends React.Components {
     state = {
    name: 'Tom'
  }

    render() {
    return (
        <div>
          <p>共享数据组件自己内部的渲染逻辑</p>
          { this.props.render(this.state) }
      </div>
    );
  }
}

// 调用方式
<DataProvider render={data => (
  <h1>Hello {data.name}</h1>
)}/>

优点:数据共享、代码复用,将组件内的state作为props传递给调用者,将渲染逻辑交给调用者
缺点:无法在 return 语句外访问数据、嵌套写法不够优雅
看下多层嵌套的情况:

const MyComponent = () => {
  return (
    <Mouse>
      {({ x, y }) => (
        <Page>
          {({ x: pageX, y: pageY }) => (
            <Connection>
              {({ api }) => {
                // yikes
              }}
            </Connection>
          )}
        </Page>
      )}
    </Mouse>
  )
};

Hook
是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。通过自定义hook,可以复用代码逻辑。

const { x, y } = useMouse();
const { x: pageX, y: pageY } = usePage();
 
useEffect(() => {
 
}, [pageX, pageY]);

优点:
1、解决hoc的prop 重名问题,hook 可以重命名
2、hook 可以让你在 return 之外使用数据
3、hook 不会嵌套,并且来源清晰
需要注意的是:hook只能在组件顶层使用,不可在分支语句中使用。

总结
Hoc、render props和hook都是为了解决代码复用的问题,但是hoc和render props都有特定的使用场景和明显的缺点。hook是react16.8更新的新的API,让组件逻辑复用更简洁明了,同时也解决了hoc和render props的一些缺点。

22、React.Component 和 React.PureComponent 的区别

PureComponent表示一个纯组件,可以用来优化React程序,减少render函数执行的次数,从而提高组件的性能。
在React中,当prop或者state发生变化时,可以通过在shouldComponentUpdate生命周期函数中执行return false来阻止页面的更新,从而减少不必要的render执行。React.PureComponent会自动执行 shouldComponentUpdate。
不过,pureComponent中的 shouldComponentUpdate() 进行的是浅比较,也就是说如果是引用数据类型的数据,只会比较不是同一个地址,而不会比较这个地址里面的数据是否一致。浅比较会忽略属性和或状态突变情况,其实也就是数据引用指针没有变化,而数据发生改变的时候render是不会执行的。如果需要重新渲染那么就需要重新开辟空间引用数据。PureComponent一般会用在一些纯展示组件上。

23、对React中Fragment的理解,它的使用场景是什么?

在React中,组件返回的元素只能有一个根元素。为了不添加多余的DOM节点,我们可以使用Fragment标签来包裹所有的元素,Fragment标签不会渲染出任何元素。

24、React实现缓存的方式有哪些?他们有什么区别?

React 组件中常用 PureComponent、React.memo、hook函数useCallback、useMemo等方法 缓存上次计算的结果。

PureComponent:主要是用在类组件中,使用PureComponent, 每次会对props进行一次浅比较,也可以在shouldComponentUpdate,去做更深层次的比对。

React.memo、hook函数useCallback、useMemo主要应用在函数组件中。

React.memo :相比于 PureComponent 可以支持指定一个参数,可以相当于shouldComponentUpdate的作用,因此 React.memo() 相对于 PureComponent 来说,用法更加方便。React.memo 一般用在「计算派生状态的代码」非常耗时的场景中,如:遍历大列表做统计信息

useCallback、useMemo 可以进行细粒度性能优化, 他们都可以缓存函数的引用或值,但是从更细的使用角度来说 useCallback 缓存函数的引用,useMemo 缓存计算数据的值。

25、React如何获取组件对应的DOM元素

第一种:React.createRef
创建React.createRef,并通过 ref 属性附加到 React 元素

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}
// 访问
const node = this.myRef.current;

第二种:回调形式的refs
React 将在组件挂载时,会调用 ref 回调函数并传入 DOM 元素,当卸载时调用它并传入 null。在 componentDidMount 或 componentDidUpdate 触发前,React 会保证 refs 一定是最新的。

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // 使用原生 DOM API 使 text 输入框获得焦点
      if (this.textInput) this.textInput.focus();
    };
  }


  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React实例上(比如 this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
      </div>
    );
  }
}

第三种:字符串类型的ref(react官方不建议使用它,因为 string 类型的 refs 存在 一些问题。它已过时并可能会在未来的版本被移除)

<p ref="info">span</p>
// 访问
this.refs.info 

26、react 事件机制

React基于浏览器的事件机制实现了一套自己的事件机制。
React 合成事件系统模拟了原生事件所有的功能,并且根据 W3C 规范来定义合成事件,兼容了所有的浏览器,拥有和浏览器原生事件相同的接口。
之所以要有自己的合成事件,因为不同浏览器的事件系统存在差异, 合成事件就是一个跨浏览器包装器,实现更好的跨平台兼容性。
同时我们在JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了 document 上,并且使用一个事件监听去处理,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当事件触发时,从数组中弹出事件对象,当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象。避免了频繁地创建和销毁事件对象解决了垃圾回收问题。

所以在react中会先执行原生事件,再执行react 事件,React 的所有事件都是挂载到 document 上的,当真实的 DOM 触发冒泡,到 document 后才会对 React 事件进行处理。如果真实DOM阻止冒泡会导致react 事件不会执行,并且react 的合成事件中不能像原生js一样直接return false ,因为return false 事件会同时preventDefault() 和 stopPropagation() ,可能产生混淆,react希望我们明确地表明其意图,提高代码的可读性,需要明确的调用preventDefault()来阻止默认行为,执行完react 事件最后执行真正在 document 上挂载的事件。

26、合成事件和普通事件的区别

1、对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰;
2、对于事件函数处理语法,原生事件为字符串,react 事件为函数;

<!DOCTYPE html>
<html>
<head>
  <title>原生事件示例</title>
</head>
<body>
  <button id="myButton" onclick="handleClick()">点击我</button>
  <script>
    function handleClick() {
      alert('原生事件触发!');
    }
  </script>
</body>
</html>
import React from 'react';

function MyButton() {
  function handleClick() {
    alert('React事件触发!');
  }

  return (
    <button onClick={handleClick}>点击我</button>
  );
}

3、react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。
在原生的js事件中return false 实际上会同时调用 event.preventDefault() 和 event.stopPropagation()由于这个行为可能导致不预期的副作用,因此 React 的事件系统选择只调用一个方法来明确表达阻止默认行为的意图。所以当需要阻止默认行为时调用preventDefault(),阻止冒泡调用event.stopPropagation

27、使用合成事件有什么优点

React 的合成事件系统是为了解决浏览器原生事件系统的一些问题而设计的
1、兼容所有浏览器,更好的跨平台;在React中,事件处理程序将传递SyntheticEvent的实例,这是一个跨浏览器原生事件包装器,它根据 W3C 规范 来定义合成事件,在底层抹平了不同浏览器的差异,在上层面向开发者暴露统一的、稳定的、与DOM原生事件相同的事件接口。
2、将事件统一存放在一个数组,便于统一管理
在浏览器原生事件系统中,每个事件监听器都是单独添加到 DOM 元素上的。这意味着如果你为同一个元素添加多个事件监听器,每个监听器都会有一个独立的引用。当这个元素被销毁时,你需要显式地移除所有这些事件监听器,否则会造成内存泄漏。
而 React 的合成事件系统通过将所有事件监听器统一存放在一个数组中来避免这个问题。当你为一个元素添加事件监听器时,React 会在内部维护一个监听器数组。当这个元素被销毁时,React 只需要简单地清空这个数组,而不需要逐个移除监听器。这样就避免了内存泄漏的问题。

28、react中事件执行的顺序

先执行原生的事件,例如

 this.childRefs.current.addEventListener

再执行react 事件,React 的所有事件都是挂载到 document 上的,当真实的 DOM 触发冒泡,到 document 后才会对 React 事件进行处理。例如:

<input onClick={submit}/>
const submit = () => {
  console.log('react 原生事件')
}

最后执行真正在 document 上挂载的事件

document.addEventListener('click',() => {
  console.log('docment')
})

合成事件会冒泡到 document 上,所以尽量避免原生事件和合成事件混用。如果原生事件阻止冒泡,那么就会导致合成事件不执行

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

不可以,render 阶段 DOM 还没有生成,无法获取 DOM。

30、类组件与函数组件有什么异同?

1、设计思想上,类组件是面向对象编程思想,我们需要继承React.Component并调用render方法返回一个react元素;而函数组件是函数式编程思想,接收一个props可以直接调用,返回一个新的React元素。
2、类组件使用state对象定义状态变量,有诸如componentDidMount、shouldComponentUpdate等生命周期钩子函数;而函数组件没有this,使用一系列的内置hooks实现对应的功能,比如使用useState创建状态变量,使用useEffect实现类似于componentDidMount、shouldComponentUpdate等生命周期钩子函数的功能。
3、缓存方式不同,类组件可以通过pureCompoment 和 shouldComponentUpdata 来缓存,函数式组件可以使用React.memo 、 useMemo、useCallBack 缓存
4、实现复用的方式不同,类组件使用hoc(高阶组件)、render props实现组件的逻辑复用、拓展组件的功能;而函数组件使用自定义hooks实现组件的逻辑复用。

31、react 什么时候会重新渲染,重新渲染时发生了什么

1、state 状态发生变化
2、父组件重新渲染引发子组件重新渲染
3、props 改变
重新渲染时react 会生成新的虚拟DOM树,和旧的虚拟DOM使用diff算法对比,将差异放入一个对象中,然后遍历该对象,更新真实DOM

32、有状态组件和无状态组件的区别

1、有状态组件通常指的是类组件,类组件可以维护自身的状态变量,即组件的 state ,类组件还有不同的生命周期方法。
2、无状态组件可以是类组件也可以是函数式组件,优先设置为函数组件,无状态组件内部不维护state,只根据外部组件传入的props进行渲染的组件,当props改变时,组件重新渲染。无状态组件不能使用ref 和生命周期方法,如自定义的

你可能感兴趣的:(前端面经&面试题,react.js,前端,前端框架)