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

7次阅读

共计 7300 个字符,预计需要花费 19 分钟才能阅读完成。

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 的状况。

正文完
 0