首页 > 其他分享 >高级前端常考react面试题指南

高级前端常考react面试题指南

时间:2023-05-19 13:11:40浏览次数:47  
标签:React 面试题 常考 表单 react state 组件 ref 节点

pureComponent和FunctionComponent区别

PureComponentComponent完全相同,但是在shouldComponentUpdate实现中,PureComponent使用了propsstate的浅比较。主要作用是用来提高某些特定场景的性能

为什么虚拟DOM会提高性能

虚拟DOM 相当于在js 和 真实DOM中间加了一个缓存,利用DOM Diff 算法避免了没有必要的DOM操作,从而提高性能

为什么 JSX 中的组件名要以大写字母开头

因为 React 要知道当前渲染的是组件还是 HTML 元素

useEffect(fn, []) 和 componentDidMount 有什么差异

useEffect 会捕获 props 和 state。所以即便在回调函数里,你拿到的还是初始的 props 和 state。如果想得到“最新”的值,可以使用 ref。

React 中 refs 干嘛用的?

Refs 提供了一种访问在render方法中创建的 DOM 节点或者 React 元素的方法。在典型的数据流中,props 是父子组件交互的唯一方式,想要修改子组件,需要使用新的pros重新渲染它。凡事有例外,某些情况下咱们需要在典型数据流外,强制修改子代,这个时候可以使用 Refs
咱们可以在组件添加一个 ref 属性来使用,该属性的值是一个回调函数,接收作为其第一个参数的底层 DOM 元素或组件的挂载实例。

class UnControlledForm extends Component {
  handleSubmit = () => {
    console.log("Input Value: ", this.input.value);
  };
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text" ref={(input) => (this.input = input)} />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

请注意,input 元素有一个ref属性,它的值是一个函数。该函数接收输入的实际 DOM 元素,然后将其放在实例上,这样就可以在 handleSubmit 函数内部访问它。
经常被误解的只有在类组件中才能使用 refs,但是refs也可以通过利用 JS 中的闭包与函数组件一起使用。

function CustomForm({ handleSubmit }) {
  let inputElement;
  return (
    <form onSubmit={() => handleSubmit(inputElement.value)}>
      <input type="text" ref={(input) => (inputElement = input)} />
      <button type="submit">Submit</button>
    </form>
  );
}

React.forwardRef有什么用

forwardRef

  • 使用forwardRefforward在这里是「传递」的意思)后,就能跨组件传递ref
  • 在例子中,我们将inputRefForm跨组件传递到MyInput中,并与input产生关联
const MyInput = forwardRef((props, ref) => {
  return <input {...props} ref={ref} />;
});

function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <MyInput ref={inputRef} />
      <button onClick={handleClick}>
        Focus the input
      </button>
    </>
  );
}

useImperativeHandle

除了「限制跨组件传递ref」外,还有一种「防止ref失控的措施」,那就是useImperativeHandle,他的逻辑是这样的:既然「ref失控」是由于「使用了不该被使用的DOM方法」(比如appendChild),那我可以限制「ref中只存在可以被使用的方法」。用useImperativeHandle修改我们的MyInput组件:

const MyInput = forwardRef((props, ref) => {
  const realInputRef = useRef(null);
  useImperativeHandle(ref, () => ({
    focus() {
      realInputRef.current.focus();
    },
  }));
  return <input {...props} ref={realInputRef} />;
});

现在,Form组件中通过inputRef.current只能取到如下数据结构:

{
  focus() {
    realInputRef.current.focus();
  },
}

就杜绝了「开发者通过ref取到DOM后,执行不该被使用的API,出现ref失控」的情况

  • 为了防止错用/滥用导致ref失控,React限制「默认情况下,不能跨组件传递ref」
  • 为了破除这种限制,可以使用forwardRef
  • 为了减少refDOM的滥用,可以使用useImperativeHandle限制ref传递的数据结构。

参考 前端进阶面试题详细解答

什么情况下使用异步组件

  • 提高页面加载速度,使用reloadable把各个页面分别单独打包,按需加载

React有哪些优化性能的手段

类组件中的优化手段

  • 使用纯组件 PureComponent 作为基类。
  • 使用 React.memo 高阶函数包装组件。
  • 使用 shouldComponentUpdate 生命周期函数来自定义渲染逻辑。

方法组件中的优化手段

  • 使用 useMemo
  • 使用 useCallBack

其他方式

  • 在列表需要频繁变动时,使用唯一 id 作为 key,而不是数组下标。
  • 必要时通过改变 CSS 样式隐藏显示组件,而不是通过条件判断显示隐藏组件。
  • 使用 Suspense 和 lazy 进行懒加载,例如:
import React, { lazy, Suspense } from "react";

export default class CallingLazyComponents extends React.Component {
  render() {
    var ComponentToLazyLoad = null;

    if (this.props.name == "Mayank") {
      ComponentToLazyLoad = lazy(() => import("./mayankComponent"));
    } else if (this.props.name == "Anshul") {
      ComponentToLazyLoad = lazy(() => import("./anshulComponent"));
    }

    return (
      <div>
        <h1>This is the Base User: {this.state.name}</h1>
        <Suspense fallback={<div>Loading...</div>}>
          <ComponentToLazyLoad />
        </Suspense>
      </div>
    )
  }
}

这三个点(...)在 React 干嘛用的?

... 在React(使用JSX)代码中做什么?它叫什么?

<Modal {...this.props} title='Modal heading' animation={false}/>

这个叫扩展操作符号或者展开操作符,例如,如果this.props包含a:1b:2,则

<Modal {...this.props} title='Modal heading' animation={false}>

等价于下面内容:

<Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}>

扩展符号不仅适用于该用例,而且对于创建具有现有对象的大多数(或全部)属性的新对象非常方便,在更新state 咱们就经常这么做:

this.setState((prevState) => {
  return { foo: { ...prevState.foo, a: "updated" } };
});

shouldComponentUpdate有什么用?为什么它很重要?

组件状态数据或者属性数据发生更新的时候,组件会进入存在期,视图会渲染更新。在生命周期方法 should ComponentUpdate中,允许选择退出某些组件(和它们的子组件)的和解过程。
和解的最终目标是根据新的状态,以最有效的方式更新用户界面。如果我们知道用户界面的某一部分不会改变,那么没有理由让 React弄清楚它是否应该更新渲染。通过在 shouldComponentUpdate方法中返回 false, React将让当前组件及其所有子组件保持与当前组件状态相同。

diff算法是怎么运作

每一种节点类型有自己的属性,也就是prop,每次进行diff的时候,react会先比较该节点类型,假如节点类型不一样,那么react会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较prop是否有更新,假如有prop不一样,那么react会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点

约束性组件( controlled component)与非约束性组件( uncontrolled component)有什么区别?

在 React中,组件负责控制和管理自己的状态。
如果将HTML中的表单元素( input、 select、 textarea等)添加到组件中,当用户与表单发生交互时,就涉及表单数据存储问题。根据表单数据的存储位置,将组件分成约東性组件和非约東性组件。
约束性组件( controlled component)就是由 React控制的组件,也就是说,表单元素的数据存储在组件内部的状态中,表单到底呈现什么由组件决定。
如下所示, username没有存储在DOM元素内,而是存储在组件的状态中。每次要更新 username时,就要调用 setState更新状态;每次要获取 username的值,就要获取组件状态值。

class App extends Component {
  //初始化状态
  constructor(props) {
    super(props);
    this.state = {
      username: "有课前端网",
    };
  }
  //查看结果
  showResult() {
    //获取数据就是获取状态值
    console.log(this.state.username);
  }
  changeUsername(e) {
    //原生方法获取
    var value = e.target.value;
    //更新前,可以进行脏值检测
    //更新状态
    this.setState({
      username: value,
    });
  }
  //渲染组件
  render() {
    //返回虚拟DOM
    return (
      <div>
        <p>
          {/*输入框绑定va1ue*/}
          <input type="text" onChange={this.changeUsername.bind(this)} value={this.state.username} />
        </p>
        <p>
          <button onClick={this.showResult.bind(this)}>查看结果</button>
        </p>
      </div>
    );
  }
}

非约束性组件( uncontrolled component)就是指表单元素的数据交由元素自身存储并处理,而不是通过 React组件。表单如何呈现由表单元素自身决定。
如下所示,表单的值并没有存储在组件的状态中,而是存储在表单元素中,当要修改表单数据时,直接输入表单即可。有时也可以获取元素,再手动修改它的值。当要获取表单数据时,要首先获取表单元素,然后通过表单元素获取元素的值。
注意:为了方便在组件中获取表单元素,通常为元素设置ref属性,在组件内部通过refs属性获取对应的DOM元素。

class App extends Component {
  //查看结果
  showResult() {
    //获取值
    console.log(this.refs.username.value);
    //修改值,就是修改元素自身的值
    this.refs.username.value = "专业前端学习平台";
    //渲染组件
    //返回虚拟DOM
    return (
      <div>
        <p>
          {/*非约束性组件中,表单元素通过 defaultvalue定义*/}
          <input type="text" ref=" username" defaultvalue="有课前端网" />
        </p>
        <p>
          <button onClick={this.showResult.bind(this)}>查看结果</button>
        </p>
      </div>
    );
  }
}

虽然非约東性组件通常更容易实现,可以通过refs直接获取DOM元素,并获取其值,但是 React建议使用约束性组件。主要原因是,约東性组件支持即时字段验证,允许有条件地禁用/启用按钮,强制输入格式等。

对有状态组件和无状态组件的理解及使用场景

(1)有状态组件

特点:

  • 是类组件
  • 有继承
  • 可以使用this
  • 可以使用react的生命周期
  • 使用较多,容易频繁触发生命周期钩子函数,影响性能
  • 内部使用 state,维护自身状态的变化,有状态组件根据外部组件传入的 props 和自身的 state进行渲染。

使用场景:

  • 需要使用到状态的。
  • 需要使用状态操作组件的(无状态组件的也可以实现新版本react hooks也可实现)

总结: 类组件可以维护自身的状态变量,即组件的 state ,类组件还有不同的生命周期方法,可以让开发者能够在组件的不同阶段(挂载、更新、卸载),对组件做更多的控制。类组件则既可以充当无状态组件,也可以充当有状态组件。当一个类组件不需要管理自身状态时,也可称为无状态组件。

(2)无状态组件 特点:

  • 不依赖自身的状态state
  • 可以是类组件或者函数组件。
  • 可以完全避免使用 this 关键字。(由于使用的是箭头函数事件无需绑定)
  • 有更高的性能。当不需要使用生命周期钩子时,应该首先使用无状态函数组件
  • 组件内部不维护 state ,只根据外部组件传入的 props 进行渲染的组件,当 props 改变时,组件重新渲染。

使用场景:

  • 组件不需要管理 state,纯展示

优点:

  • 简化代码、专注于 render
  • 组件不需要被实例化,无生命周期,提升性能。 输出(渲染)只取决于输入(属性),无副作用
  • 视图和数据的解耦分离

缺点:

  • 无法使用 ref
  • 无生命周期方法
  • 无法控制组件的重渲染,因为无法使用shouldComponentUpdate 方法,当组件接受到新的属性时则会重渲染

总结: 组件内部状态且与外部无关的组件,可以考虑用状态组件,这样状态树就不会过于复杂,易于理解和管理。当一个组件不需要管理自身状态时,也就是无状态组件,应该优先设计为函数组件。比如自定义的 <Button/><Input /> 等组件。

在 Reducer文件里,对于返回的结果,要注意哪些问题?

在 Reducer文件里,对于返回的结果,必须要使用 Object.assign ( )来复制一份新的 state,否则页面不会跟着数据刷新。

return Object.assign({}, state, {
  type: action.type,
  shouldNotPaint: true,
});

怎么阻止组件的渲染

在组件的 render 方法中返回 null 并不会影响触发组件的生命周期方法

React中constructor和getInitialState的区别?

两者都是用来初始化state的。前者是ES6中的语法,后者是ES5中的语法,新版本的React中已经废弃了该方法。

getInitialState是ES5中的方法,如果使用createClass方法创建一个Component组件,可以自动调用它的getInitialState方法来获取初始化的State对象,

var APP = React.creatClass ({
  getInitialState() {
    return { 
        userName: 'hi',
        userId: 0
     };
 }
})

React在ES6的实现中去掉了getInitialState这个hook函数,规定state在constructor中实现,如下:

Class App extends React.Component{
    constructor(props){
      super(props);
      this.state={};
    }
  }

diff算法如何比较?

  • 只对同级比较,跨层级的dom不会进行复用
  • 不同类型节点生成的dom树不同,此时会直接销毁老节点及子孙节点,并新建节点
  • 可以通过key来对元素diff的过程提供复用的线索
  • 单节点diff
  • 单点diff有如下几种情况:
  • key和type相同表示可以复用节点
  • key不同直接标记删除节点,然后新建节点
  • key相同type不同,标记删除该节点和兄弟节点,然后新创建节点

React的Fiber工作原理,解决了什么问题

  • React Fiber 是一种基于浏览器的单线程调度算法。

React Fiber 用类似 requestIdleCallback 的机制来做异步 diff。但是之前数据结构不支持这样的实现异步 diff,于是 React 实现了一个类似链表的数据结构,将原来的 递归diff 变成了现在的 遍历diff,这样就能做到异步可更新了

React- Router有几种形式?

有以下几种形式。
HashRouter,通过散列实现,路由要带#。
BrowerRouter,利用HTML5中 history API实现,需要服务器端支持,兼容性不是很好。

React.Children.map和js的map有什么区别?

JavaScript中的map不会对为null或者undefined的数据进行处理,而React.Children.map中的map可以处理React.Children为null或者undefined的情况。

标签:React,面试题,常考,表单,react,state,组件,ref,节点
From: https://www.cnblogs.com/beifeng1996/p/17414814.html

相关文章

  • react-query-builder查询构建器中文文档
    简介react-query-builder是一个React组件库,用于构建可配置的查询构建器。使用react-query-builder,您可以轻松地构建复杂的查询表单,使用户能够以直观的方式构建和执行查询。以下是一些常见的用例:构建高级搜索表单,允许用户根据不同的条件搜索数据。在数据可视化应用程序中使用......
  • python面试题汇总
    以下是一些常见的Python面试题以及它们的答案:Python中的GIL是什么,有何作用?GIL全称GlobalInterpreterLock,是Python解释器在多线程情况下保证线程安全的一种机制。GIL确保同一时间只有一个线程可以执行Python字节码,从而避免了由于多个线程同时修改共享数据造成的竞态条件问题。解释......
  • Java面试题总结
    spring事务的传播机制 spring事务失效原因1,数据库不支持事务:比如mysql数据库,MyISAM(马儿爱涩m)引擎不持支事务,需要使用InnoDB引擎才行2,该类不受spring管理,使用@Transactional会失效:因为spring事务是基于aop动态代理实现的,需要从ioc容器中获取bean才能为该对象生成代理类,从而......
  • APP中RN页面热更新流程-ReactNative源码分析
    平时使用WebStorm或VSCode对RN工程中的文件修改后,在键盘上按一下快捷cmd+s进行文件保存,此时当前调试的RN页面就会自动进行刷新,这是RN开发相比于原生开发一个很大的优点:热更新。那么,从按一下快捷cmd+s到RN页面展示出最新的JS页面,这个过程是怎样发生的呢?下面根据时间顺序来梳理一下......
  • java面试题--Redis
    一、说一下redis的持久化机制原理?RDB文件:redisdatabase。存储的是某个时间点的数据库内容的快照,是结果。redis默认的持久化策略。落盘策略:使用SAVE或者BGSAVE命令。(1)SAVE:有主线程执行,会阻塞客户端。(2)BGSAVE:会fork出一个子进程,不会出现阻塞问题。子进程使用写时拷贝的策......
  • quasar [email protected] ssr Meta插件 使用 title无法reactive的bug
    为了使自己的网站SEO更友好,在项目里启用了quasar的Meta插件。但实际使用下来发现文档里的描述不正确。为了动态的更新title和meta信息,文档https://quasar.dev/quasar-plugins/meta里介绍ReactiveInthesectionabove,younoticedallofthemetapropsare“static”.But......
  • JVM(四)虚拟机栈(三)虚拟机栈面试题
    JVM(四)虚拟机栈(三)虚拟机栈面试题1举例栈溢出的情况?当方法调用不停将栈帧压入虚拟机栈导致栈内空间不足而出现StackOverFlowError即是出现了栈溢出可以通过-Xss设置栈的大小,栈的大小可以是固定的也可以是动态变化的,如果固定且超出设定值则就会出现栈溢出;如果是动态变化的,栈空......
  • React.FC中父组件调用子组件方法
    https://blog.csdn.net/qq_36990322/article/details/1098588901.函数式和hooks写法其实下面的缺点基本不算缺点了,因为函数式写法,下面算是简单的了。使用forwardRef只会让你的组件定义的更复杂优点:1、写法简单易懂2、假如子组件嵌套了HOC,也可以指向真实子组件缺点:1、需要自定......
  • 部署带路由的React SPA 项目
    使用Nginx将/phone/*请求反向代理为/*启动项目serve-s./build-l3000项目内路由仍然是/phone/xxx不用改动,但需要在package.json中,指定静态资源根目录{..."homepage":"/phone/",...}......
  • 【React+Antd】 多选下拉框禁止输入
    <Selectoptions={}onChange={provinceChange}allowClearmode="multiple"//showSearch={false}maxTagTextLength={4}/>mode设置为多选,multiple配合showSearch可以实现禁止选择,mode设置为tags配合showSerach不能实现禁止输入......