前端必会react面试题合集2

2023-01-04 15:39:49 浏览数 (1)

调用 setState 之后发生了什么

在代码中调用 setState 函数之后,React 会将传入的参数与之前的状态进行合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。在 React 得到元素树之后,React 会计算出新的树和老的树之间的差异,然后根据差异对界面进行最小化重新渲染。通过 diff 算法,React 能够精确制导哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

  • 在 setState 的时候,React 会为当前节点创建一个 updateQueue 的更新列队。
  • 然后会触发 reconciliation 过程,在这个过程中,会使用名为 Fiber 的调度算法,开始生成新的 Fiber 树, Fiber 算法的最大特点是可以做到异步可中断的执行。
  • 然后 React Scheduler 会根据优先级高低,先执行优先级高的节点,具体是执行 doWork 方法。
  • 在 doWork 方法中,React 会执行一遍 updateQueue 中的方法,以获得新的节点。然后对比新旧节点,为老节点打上 更新、插入、替换 等 Tag。
  • 当前节点 doWork 完成后,会执行 performUnitOfWork 方法获得新节点,然后再重复上面的过程。
  • 当所有节点都 doWork 完成后,会触发 commitRoot 方法,React 进入 commit 阶段。
  • 在 commit 阶段中,React 会根据前面为各个节点打的 Tag,一次性更新整个 dom 元素

可以使用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 )

react 实现一个全局的 dialog

代码语言:javascript复制
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import ReactDOM from 'react-dom';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './dialog.css';
let defaultState = {
  alertStatus:false,
  alertTip:"提示",
  closeDialog:function(){},
  childs:''
}
class Dialog extends Component{
  state = {
    ...defaultState
  };
  // css动画组件设置为目标组件
  FirstChild = props => {
    const childrenArray = React.Children.toArray(props.children);
    return childrenArray[0] || null;
  }
  //打开弹窗
  open =(options)=>{
    options = options || {};
    options.alertStatus = true;
    var props = options.props || {};
    var childs = this.renderChildren(props,options.childrens) || '';
    console.log(childs);
    this.setState({
      ...defaultState,
      ...options,
      childs
    })
  }
  //关闭弹窗
  close(){
    this.state.closeDialog();
    this.setState({
      ...defaultState
    })
  }
  renderChildren(props,childrens) {
    //遍历所有子组件
    var childs = [];
    childrens = childrens || [];
    var ps = {
        ...props,  //给子组件绑定props
        _close:this.close  //给子组件也绑定一个关闭弹窗的事件    
       };
    childrens.forEach((currentItem,index) => {
        childs.push(React.createElement(
            currentItem,
            {
                ...ps,
                key:index
            }
        ));
    })
    return childs;
  }
  shouldComponentUpdate(nextProps, nextState){
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
   
  render(){
    return (
      <ReactCSSTransitionGroup
        component={this.FirstChild}
        transitionName='hide'
        transitionEnterTimeout={300}
        transitionLeaveTimeout={300}>
        <div className="dialog-con" style={this.state.alertStatus? {display:'block'}:{display:'none'}}>
            {this.state.childs}        </div>
      </ReactCSSTransitionGroup>
    );
  }
}
let div = document.createElement('div');
let props = {
   
};
document.body.appendChild(div);
let Box = ReactD

子类:

代码语言:javascript复制
//子类jsx
import React, { Component } from 'react';
class Child extends Component {
    constructor(props){
        super(props);
        this.state = {date: new Date()};
  }
  showValue=()=>{
    this.props.showValue && this.props.showValue()
  }
  render() {
    return (
      <div className="Child">
        <div className="content">
           Child           <button onClick={this.showValue}>调用父的方法</button>
        </div>
      </div>
    );
  }
}
export default Child;

css:

代码语言:css复制
.dialog-con{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.3);
}

class类的key改了,会发生什么,会执行哪些周期函数?

在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。

答:componentWillMount componentDidMount render

何为 redux

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

指出(组件)生命周期方法的不同

  • componentWillMount -- 多用于根组件中的应用程序配置
  • componentDidMount -- 在这可以完成所有没有 DOM 就不能做的所有配置,并开始获取所有你需要的数据;如果需要设置事件监听,也可以在这完成
  • componentWillReceiveProps -- 这个周期函数作用于特定的 prop 改变导致的 state 转换
  • shouldComponentUpdate -- 如果你担心组件过度渲染,shouldComponentUpdate 是一个改善性能的地方,因为如果组件接收了新的 prop, 它可以阻止(组件)重新渲染。shouldComponentUpdate 应该返回一个布尔值来决定组件是否要重新渲染
  • componentWillUpdate -- 很少使用。它可以用于代替组件的 componentWillReceivePropsshouldComponentUpdate(但不能访问之前的 props)
  • componentDidUpdate -- 常用于更新 DOM,响应 prop 或 state 的改变
  • componentWillUnmount -- 在这你可以取消网络请求,或者移除所有与组件相关的事件监听器

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

何为 reducer

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

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

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

  • 处理焦点、文本选择或者媒体的控制
  • 触发必要的动画
  • 集成第三方 DOM 库

Refs 是使用 React.createRef() 方法创建的,他通过 ref 属性附加到 React 元素上。要在整个组件中使用 Refs,需要将 ref 在构造函数中分配给其实例属性:

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

由于函数组件没有实例,因此不能在函数组件上直接使用 ref

代码语言:javascript复制
function MyFunctionalComponent() {
  return <input />;
}
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // 这将不会工作!
    return (
      <MyFunctionalComponent ref={this.textInput} />
    );
  }
}

但可以通过闭合的帮助在函数组件内部进行使用 Refs:

代码语言:javascript复制
function CustomTextInput(props) {
  // 这里必须声明 textInput,这样 ref 回调才可以引用它
  let textInput = null;
  function handleClick() {
    textInput.focus();
  }
  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

注意:

  • 不应该过度的使用 Refs
  • ref 的返回值取决于节点的类型:
    • ref 属性被用于一个普通的 HTML 元素时,React.createRef() 将接收底层 DOM 元素作为他的 current 属性以创建 ref
    • ref 属性被用于一个自定义的类组件时,ref 对象将接收该组件已挂载的实例作为他的 current
  • 当在父组件中需要访问子组件中的 ref 时可使用传递 Refs 或回调 Refs。

Component, Element, Instance 之间有什么区别和联系?

  • 元素: 一个元素element是一个普通对象(plain object),描述了对于一个DOM节点或者其他组件component,你想让它在屏幕上呈现成什么样子。元素element可以在它的属性props中包含其他元素(译注:用于形成元素树)。创建一个React元素element成本很低。元素element创建之后是不可变的。
  • 组件: 一个组件component可以通过多种方式声明。可以是带有一个render()方法的类,简单点也可以定义为一个函数。这两种情况下,它都把属性props作为输入,把返回的一棵元素树作为输出。
  • 实例: 一个实例instance是你在所写的组件类component class中使用关键字this所指向的东西(译注:组件实例)。它用来存储本地状态和响应生命周期事件很有用。

函数式组件(Functional component)根本没有实例instance。类组件(Class component)有实例instance,但是永远也不需要直接创建一个组件的实例,因为React帮我们做了这些。

在构造函数调用 super 并将 props 作为参数传入的作用

在调用 super() 方法之前,子类构造函数无法使用this引用,ES6 子类也是如此。

将 props 参数传递给 super() 调用的主要原因是在子构造函数中能够通过this.props来获取传入的 props

传递了props

代码语言:javascript复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    console.log(this.props); // { name: 'sudheer',age: 30 }
  }
}

没传递 props

代码语言:javascript复制
class MyComponent extends React.Component {
  constructor(props) {
    super();
    console.log(this.props); // undefined
    // 但是 Props 参数仍然可用
    console.log(props); // Prints { name: 'sudheer',age: 30 }
  }
  render() {
    // 构造函数外部不受影响
    console.log(this.props); // { name: 'sudheer',age: 30 }
  }
}

react-router4的核心

  • 路由变成了组件
  • 分散到各个页面,不需要配置 比如<link> <route></route>

如何在React中使用innerHTML

增加dangerouslySetInnerHTML属性,并且传入对象的属性名叫_html

代码语言:javascript复制
function Component(props){
    return <div dangerouslySetInnerHTML={{_html:'<span>你好</span>'}}>
    </div>
}

展示组件(Presentational component)和容器组件(Container component)之间有何不同

展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。

容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。

为什么要使用 React. Children. map( props. children,( )=>)而不是props. children. map ( ( ) => )?

因为不能保证 props. children将是一个数组。

以下面的代码为例。

代码语言:javascript复制
<Parent>
    <h1>有课前端网</h1>
</Parent>

在父组件内部,如果尝试使用 props.children. map映射子对象,则会抛出错误,因为props. children是一个对象,而不是一个数组。

如果有多个子元素, React会使 props.children成为一个数组,如下所示。

代码语言:javascript复制
<Parent>
  <h1>有课前端网</h1>
  <h2>前端技术学习平台</h2>
</Parent>;
//不建议使用如下方式,在这个案例中会抛出错误。

class Parent extends Component {
  render() {
    return <div> {this.props.children.map((obj) => obj)}</div>;
  }
}

建议使用如下方式,避免在上一个案例中抛出错误。

代码语言:javascript复制
class Parent extends Component {
  render() {
    return <div> {React.Children.map(this.props.children, (obj) => obj)}</div>;
  }
}

vue 或者react 优化整体优化

  1. 虚拟dom

为什么虚拟 dom 会提高性能?(必考)

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

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。

React的事件和普通的HTML事件有什么不同?

区别:

  • 对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰;
  • 对于事件函数处理语法,原生事件为字符串,react 事件为函数;
  • react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。

合成事件是 react 模拟原生 DOM 事件所有能力的一个事件对象,其优点如下:

  • 兼容所有浏览器,更好的跨平台;
  • 将事件统一存放在一个数组,避免频繁的新增与删除(垃圾回收)。
  • 方便 react 统一管理和事务机制。

事件的执行顺序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为需要冒泡到document 上合成事件才会执行。

react 强制刷新

component.forceUpdate() 一个不常用的生命周期方法, 它的作用就是强制刷新

官网解释如下

默认情况下,当组件的 state 或 props 发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染。

调用 forceUpdate() 将致使组件调用 render() 方法,此操作会跳过该组件的 shouldComponentUpdate()。但其子组件会触发正常的生命周期方法,包括 shouldComponentUpdate() 方法。如果标记发生变化,React 仍将只更新 DOM。

通常你应该避免使用 forceUpdate(),尽量在 render() 中使用 this.props 和 this.state。

shouldComponentUpdate 在初始化 和 forceUpdate 不会执行

ssr原理是什么?

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

diff 算法?

  • 把树形结构按照层级分解,只比较同级元素
  • 给列表结构的每个单元添加唯一的 key 属性,方便比较
  • React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
  • 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个 事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
  • 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

React声明组件有哪几种方法,有什么不同?

React 声明组件的三种方式:

  • 函数式定义的无状态组件
  • ES5原生方式React.createClass定义的组件
  • ES6形式的extends React.Component定义的组件

(1)无状态函数式组件 它是为了创建纯展示组件,这种组件只负责根据传入的props来展示,不涉及到state状态的操作

组件不会被实例化,整体渲染性能得到提升,不能访问this对象,不能访问生命周期的方法

(2)ES5 原生方式 React.createClass // RFC React.createClass会自绑定函数方法,导致不必要的性能开销,增加代码过时的可能性。

(3)E6继承形式 React.Component // RCC 目前极为推荐的创建有状态组件的方式,最终会取代React.createClass形式;相对于 React.createClass可以更好实现代码复用。

无状态组件相对于于后者的区别: 与无状态组件相比,React.createClass和React.Component都是创建有状态的组件,这些组件是要被实例化的,并且可以访问组件的生命周期方法。

React.createClass与React.Component区别:

① 函数this自绑定

  • React.createClass创建的组件,其每一个成员函数的this都有React自动绑定,函数中的this会被正确设置。
  • React.Component创建的组件,其成员函数不会自动绑定this,需要开发者手动绑定,否则this不能获取当前组件实例对象。

② 组件属性类型propTypes及其默认props属性defaultProps配置不同

  • React.createClass在创建组件时,有关组件props的属性类型及组件默认的属性会作为组件实例的属性来配置,其中defaultProps是使用getDefaultProps的方法来获取默认组件属性的
  • React.Component在创建组件时配置这两个对应信息时,他们是作为组件类的属性,不是组件实例的属性,也就是所谓的类的静态属性来配置的。

③ 组件初始状态state的配置不同

  • React.createClass创建的组件,其状态state是通过getInitialState方法来配置组件相关的状态;
  • React.Component创建的组件,其状态state是在constructor中像初始化组件属性一样声明的。

0 人点赞