35道必须要清楚的 React面试题

来源:http://www.chinese-glasses.com 作者:Web前端 人气:181 发布时间:2020-04-15
摘要:时间: 2019-10-07阅读: 107标签: 面试问题1:什么是虚拟DOM?主题:React难度: ⭐ React入门 本文介绍React的相关概念,使用方式。目的是使读者对React有一个大致的了解,以便于能够更加深入的

时间: 2019-10-07阅读: 107标签: 面试问题1:什么是虚拟DOM?主题: React难度: ⭐

React入门

本文介绍React的相关概念,使用方式。目的是使读者对React有一个大致的了解,以便于能够更加深入的学习、快速上手React。学习React要掌握其中的几个重要的概念JSX、元素(事件处理)、组件(props、state、ref、受控组件、生命周期)。

虚拟 DOM (VDOM)是真实 DOM 在内存中的表示。UI 的表示形式保存在内存中,并与实际的 DOM 同步。这是一个发生在渲染函数被调用和元素在屏幕上显示之间的步骤,整个过程被称为调和

React优缺点

React是一个前端框架,学习框架必然要了解框架的优缺点、以便根据不同的项目采用合适的框架。
优点:

  • 能够实现服务器端的渲染,便于搜索引擎优化。
  • 能够很好的和现有的代码结合。React只是MVC中的View层,对于其他的部分并没有硬性要求。
  • 组件化的开发方式,能够更好的复用代码、更好的定位bug。
  • 使用virtual dom,性能很好

缺点:

  • 不是完整的开发框架需要加上其他东西才能写大型应用。

问题2:类组件和函数组件之间的区别是啥?主题: React难度: ⭐⭐类组件可以使用其他特性,如状态state和生命周期钩子。当组件只是接收props渲染到页面时,就是无状态组件,就属于函数组件,也被称为哑组件或展示组件。

JSX

JSX是JavaScript Syntax eXtension的缩写,它是一种在JavaScript中嵌入XML的React语法,类似于E4X。React推荐使用JSX来描述用户界面

JSX语法需要写在<script type="text/babel"></script>标签中或者使用babel转码器进行转码

JSX基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。

JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员

<script type="text/babel">
var arr = [
  <h1>Hello world!</h1>,
  <h2>React is awesome</h2>,
];
ReactDOM.render(
  <div>{arr}</div>,
  document.getElementById('example')
);
</script>

函数组件和类组件当然是有区别的,而且函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。

元素

元素是构成React应用的最小单位,React中元素时普通的对象。React DOM可以确保浏览器DOM的数据内容与React元素保持一致。

const element = <h1>Hello, world</h1>;
// 使用ReactDOM.render渲染到页面上
ReactDOM.render(
  element,
  document.getElementById('root')
);

当元素被创建之后,是无法改变其内容或属性的。如果想要改变需要将它传入ReactDOM.render()方法中。(react只会渲染必要的部分)

元素除了可以是DOM元素外,还可以是自定义的组件。

区别

事件处理

驼峰式命名,使用函数名作为事件处理函数,需要使用e.preventDefault()来阻止默认行为,不能直接使用return false阻止返回值。

<script type="text/babel">
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    e.preventDefault();
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}
</script>

e是一个合成事件不需要考虑跨浏览器的兼容性问题。

使用实验性的属性初始化器语法来绑定this

<script type="text/babel">
class LoggingButton extends React.Component {
  // This syntax ensures `this` is bound within handleClick.
  // Warning: this is *experimental* syntax.
  handleClick = () => {
    console.log('this is:', this);
  }
  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}
</script>

函数组件

组件

顾明思议是用来将UI切分成可复用且独立的部件。

函数、类定义组件:

<script type="text/babel">
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

class Welcome extends React.Component {
   constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

const element = <Welcome name="Sara" />;
</script>

组件之间可以像普通标签一样嵌套组合成新元素、新组件。

style属性的设置不能写成:style="opacity:{this.state.opacity};",而要写成style={{opacity: this.state.opacity}}。因为React组件样式是一个对象,所以第一重大括号表示这是JS语法,第二重大括号表示样式对象。

类组件

props

class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。

this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点

this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。

React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。

props的值不能够被改变。

使用propTypes来验证别人使用组件时,提供的参数是否符合要求
使用defaultProps设置默认值

Greeting.propTypes = {
    title: React.PropTypes.string.isRequired,
    //限制为一个子代
    children: PropTypes.element.isRequired
};
Greeting.defaultProps = {
    name:"test"
};

是否有this没有有是否有生命周期没有有是否有状态state没有有问题 3:React 中 refs 干嘛用的?主题: React难度: ⭐⭐

state

每当组件的状态state发生改变时,组件就可以自动的更新。

state在构造函数中初始化,使用书面语的形式,即this.state = {}

状态的更新需要使用setState(可接收对象或函数(参数是上一个状态、props))函数,不能直接使用书面语(this.state.comment = "")

this.props 和 this.state 可能是异步更新的,不能直接依靠它们来计算state,而应该是使用函数的形式。

Refs提供了一种访问在render方法中创建的 DOM 节点或者 React 元素的方法。在典型的数据流中,props是父子组件交互的唯一方式,想要修改子组件,需要使用新的pros重新渲染它。凡事有例外,某些情况下咱们需要在典型数据流外,强制修改子代,这个时候可以使用Refs。

列表 & Keys

Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。

key应该在数组中被明确出来,可以使用id或索引来作为key

<script type="text/babel">
const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);
</script>

咱们可以在组件添加一个ref属性来使用,该属性的值是一个回调函数,接收作为其第一个参数的底层 DOM 元素或组件的挂载实例。

受控组件

在HTML当中,像<input>,<textarea>, 和 <select>这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState(). 方法进行更新.

因此使用“受控组件”的形式来编写表单组件(值统一写在value属性中,更新方法写在onChange函数中)

多个受控组件的统一解决方案,动态确定state的属性

<script type="text/babel">
class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}
</script>
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 ) }}

非受控组件

在受控组件中,表单数据由 React 组件处理。如果让表单数据由 DOM 处理时,替代方案为使用非受控组件。(使用ref获取DOM并取值(必须要等到DOM插入后才能获得DOM),使用defaultValue设置默认值)

<script type="text/babel">
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.value);
    event.preventDefault();
  }
    render() {
      return (
        <form onSubmit={this.handleSubmit}>
          <label>
            Name:
            <input
              defaultValue="Bob"
              type="text"
              ref={(input) => this.input = input} />
          </label>
          <input type="submit" value="Submit" />
        </form>
      );
    }
}
</script>

请注意,input元素有一个ref属性,它的值是一个函数。该函数接收输入的实际 DOM 元素,然后将其放在实例上,这样就可以在handleSubmit函数内部访问它。

状态提升

如果多个组件中有需要共用的状态,应该将该状态设置在父组件上,来实现状态共享。

<script type="text/babel">
const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};

function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}

function tryConvert(temperature, convert) {
  const input = parseFloat(temperature);
  if (Number.isNaN(input)) {
    return '';
  }
  const output = convert(input);
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();
}

function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;
  }
  return <p>The water would not boil.</p>;
}

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}

ReactDOM.render(
  <Calculator />,
  document.getElementById('root')
);

</script>

经常被误解的只有在类组件中才能使用refs,但是refs也可以通过利用 JS 中的闭包与函数组件一起使用。

生命周期

组件的生命周期分成三个状态:

  • Mounting:已插入真实 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真实 DOM

React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。

  • componentWillMount()
  • componentDidMount()
  • componentWillUpdate(object nextProps, object nextState)
  • componentDidUpdate(object prevProps, object prevState)
  • componentWillUnmount()

此外,React 还提供两种特殊状态的处理函数。

  • componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
  • shouldComponentUpdate(object nextProps, object nextState):组件判断是否重新渲染时调用

参考文献:
React入门实例教程——阮一峰
React文档15.6
React优缺点

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

问题 4:在 React 中如何处理事件主题: React难度: ⭐⭐

为了解决跨浏览器的兼容性问题,SyntheticEvent实例将被传递给你的事件处理函数,SyntheticEvent是 React 跨浏览器的浏览器原生事件包装器,它还拥有和浏览器原生事件相同的接口,包括stopPropagation()和preventDefault()。

比较有趣的是,React 实际上并不将事件附加到子节点本身。React 使用单个事件侦听器侦听顶层的所有事件。这对性能有好处,也意味着 React 在更新 DOM 时不需要跟踪事件监听器。

问题 5:state 和 props 区别是啥?主题: React难度: ⭐⭐

props和state是普通的 JS 对象。虽然它们都包含影响渲染输出的信息,但是它们在组件方面的功能是不同的。即

state是组件自己管理数据,控制自己的状态,可变;props是外部传入的数据参数,不可变;没有state的叫做无状态组件,有state的叫做有状态组件;多用props,少用state,也就是多写无状态组件。问题 6:如何创建 refs主题: React难度: ⭐⭐

Refs 是使用React.createRef()创建的,并通过ref属性附加到 React 元素。在构造组件时,通常将Refs分配给实例属性,以便可以在整个组件中引用它们。

class MyComponent extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); } render() { return div ref={this.myRef} /; }}

或者这样用:

class UserForm extends Component { handleSubmit = () = { console.log("Input Value is: ", this.input.value) } render () { return ( form onSubmit={this.handleSubmit} input type='text' ref={(input) = this.input = input} / // Access DOM input in handle submit button type='submit'Submit/button /form ) }}

问题 7:什么是高阶组件?主题: React难度: ⭐⭐

高阶组件(HOC)是接受一个组件并返回一个新组件的函数。基本上,这是一个模式,是从 React 的组合特性中衍生出来的,称其为纯组件,因为它们可以接受任何动态提供的子组件,但不会修改或复制输入组件中的任何行为。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

HOC 可以用于以下许多用例

代码重用、逻辑和引导抽象渲染劫持state 抽象和操作props 处理问题 8:在构造函数调用super并将props作为参数传入的作用是啥?主题: React难度: ⭐⭐

在调用super()方法之前,子类构造函数无法使用this引用,ES6 子类也是如此。将props参数传递给super()调用的主要原因是在子构造函数中能够通过this.props来获取传入的props。

传递 props

class MyComponent extends React.Component { constructor(props) { super(props); console.log(this.props); // { name: 'sudheer',age: 30 } }}

没传递 props

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 } }}

上面示例揭示了一点。props的行为只有在构造函数中是不同的,在构造函数之外也是一样的。

问题 9:什么是控制组件?主题: React难度: ⭐⭐⭐

在 HTML 中,表单元素如input、textarea和select通常维护自己的状态,并根据用户输入进行更新。当用户提交表单时,来自上述元素的值将随表单一起发送。

而 React 的工作方式则不同。包含表单的组件将跟踪其状态中的输入值,并在每次回调函数(例如onChange)触发时重新渲染组件,因为状态被更新。以这种方式由 React 控制其值的输入表单元素称为受控组件

问题 10:如何 React.createElement ?主题: React难度: ⭐⭐⭐

问题:

const element = ( h1 className="greeting" Hello, world! /h1)

上述代码如何使用React.createElement来实现:

const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!');

问题 11:讲讲什么是 JSX ?主题: React难度: ⭐⭐⭐

Facebook第一次发布 React 时,他们还引入了一种新的 JS 方言JSX,将原始 HTML 模板嵌入到 JS 代码中。JSX 代码本身不能被浏览器读取,必须使用Babel和webpack等工具将其转换为传统的JS。很多开发人员就能无意识使用 JSX,因为它已经与 React 结合在一直了。

class MyComponent extends React.Component { render() { let props = this.props; return ( div className="my-component" a href={props.url}{props.name}/a /div ); }}

问题 12:根据下面定义的代码,可以找出存在的两个问题吗 ?主题: React难度: ⭐⭐⭐

请看下面的代码:

答案:

1.在构造函数没有将props传递给super,它应该包括以下行

constructor(props) { super(props); // ...}

2.事件监听器(通过addEventListener()分配时)的作用域不正确,因为 ES6 不提供自动绑定。因此,开发人员可以在构造函数中重新分配clickHandler来包含正确的绑定:

constructor(props) { super(props); this.clickHandler = this.clickHandler.bind(this); // ...}

问题 13:为什么不直接更新state呢 ?主题: React难度: ⭐⭐⭐

如果试图直接更新state,则不会重新渲染组件。

 // 错误 This.state.message = 'Hello world';

需要使用setState()方法来更新state。它调度对组件state对象的更新。当state改变时,组件通过重新渲染来响应:

// 正确做法This.setState({message: ‘Hello World’});

问题 14:React 组件生命周期有哪些不同阶段?主题: React难度: ⭐⭐⭐

在组件生命周期中有四个不同的阶段:

Initialization:在这个阶段,组件准备设置初始化状态和默认属性。Mounting:react 组件已经准备好挂载到浏览器 DOM 中。这个阶段包括componentWillMount和componentDidMount生命周期方法。Updating:在这个阶段,组件以两种方式更新,发送新的 props 和 state 状态。此阶段包括shouldComponentUpdate、componentWillUpdate和componentDidUpdate生命周期方法。Unmounting:在这个阶段,组件已经不再被需要了,它从浏览器 DOM 中卸载下来。这个阶段包含componentWillUnmount生命周期方法。

除以上四个常用生命周期外,还有一个错误处理的阶段:

Error Handling:在这个阶段,不论在渲染的过程中,还是在生命周期方法中或是在任何子组件的构造函数中发生错误,该组件都会被调用。这个阶段包含了componentDidCatch生命周期方法。

问题 15:React 的生命周期方法有哪些?主题: React难度: ⭐⭐⭐componentWillMount:在渲染之前执行,用于根组件中的 App 级配置。componentDidMount:在第一次渲染之后执行,可以在这里做AJAX请求,DOM 的操作或状态更新以及设置事件监听器。componentWillReceiveProps:在初始化render的时候不会执行,它会在组件接受到新的状态(Props)时被触发,一般用于父组件状态更新时子组件的重新渲染shouldComponentUpdate:确定是否更新组件。默认情况下,它返回true。如果确定在state或props更新后组件不需要在重新渲染,则可以返回false,这是一个提高性能的方法。componentWillUpdate:在shouldComponentUpdate返回true确定要更新组件之前件之前执行。componentDidUpdate:它主要用于更新DOM以响应props或state更改。componentWillUnmount:它用于取消任何的网络请求,或删除与组件关联的所有事件监听器。问题 16:这三个点(...)在 React 干嘛用的?主题: React难度: ⭐⭐⭐

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

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

这个叫扩展操作符号或者展开操作符,例如,如果this.props包含a:1和b: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"}};});

问题 17:使用 React Hooks 好处是啥?主题: React难度: ⭐⭐⭐

首先,Hooks 通常支持提取和重用跨多个组件通用的有状态逻辑,而无需承担高阶组件或渲染props的负担。Hooks可以轻松地操作函数组件的状态,而不需要将它们转换为类组件。

本文由10bet发布于Web前端,转载请注明出处:35道必须要清楚的 React面试题

关键词:

上一篇:JavaScript中的事件冒泡?事件传播的解释

下一篇:没有了

最火资讯