3. react 面试题

239 阅读16分钟

1.React 中 keys 的作用是什么?

Keys 是React在操作列表中元素被修改,添加,或者删除的辅助标识.

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

2.React 中 refs 的作用是什么?

refs提供了一种访问 在render方法中 创建的 DOM 节点或者 React 元素的方法。
    + 我们可以为元素添加ref属性,该属性的值是一个回调函数,接收作为其第一个参数的底层 DOM 元素或组件的挂载实例。
class CustomForm 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 属性,该属性声明的回调函数会接收 input 对应的 DOM 元素,我们将其绑定到 this 指针以便在其他的类函数中使用。
  • refs 并不是类组件的专属,函数式组件同样能够利用闭包暂存其值:
function CustomForm ({handleSubmit}) {
  let inputElement
  return (
    <form onSubmit={() => handleSubmit(inputElement.value)}>
      <input
        type='text'
        ref={(input) => inputElement = input} />
      <button type='submit'>Submit</button>
    </form>
  )
}

3.React 中有三种构建组件的方式

1.函数式组件
2.ES5原生方式React.createClass定义的组件
3.ES6形式的extends React.Component定义的组件

1.函数式组件

import React from 'react';
export default function Vote(props) {
    return <div>
        <h1>我是函数是组件</h1>
    </div>
}

ReactDOM.render(<Vote index={10} title='每天都要开心大笑~'></Vote>
   , document.getElementById('root')) 

特点:

1.简单(开发维护简单和渲染速度也快)
2.函数式组件是静态组件,一旦组件被调用,组件中的内容渲染完成,当前组件中的信息基本上就不变了
3.没有state状态管控内容,生命周期函数等随时更改组件中的内容(只有父组件重新调用它,才可能被更改)
4.不能给传递的属性直接的赋值,不能基于prop-types给属性设置默认的规则

2.React.createClass

React.createClass是react刚开始推荐的创建组件的方式,这是ES5的原生的JavaScript来实现的React组件,其形式如下:

var InputControlES5 = React.createClass({
    propTypes: {//定义传入props中的属性各种类型
        initialValue: React.PropTypes.string
    },
    defaultProps: { //组件默认的props对象
        initialValue: ''
    },
    // 设置 initial state
    getInitialState: function() {//组件相关的状态对象
        return {
            text: this.props.initialValue || 'placeholder'
        };
    },
    handleChange: function(event) {
        this.setState({ //this represents react component instance
            text: event.target.value
        });
    },
    render: function() {
        return (
            <div>
                Type something:
                <input onChange={this.handleChange} value={this.state.text} />
            </div>
        );
    }
});
InputControlES6.propTypes = {
    initialValue: React.PropTypes.string
};
InputControlES6.defaultProps = {
    initialValue: ''
};

特点:

+ React.createClass会自绑定函数方法(不像React.Component只绑定需要关心的函数)导致不必要的性能开销,增加代码过时的可能性。
+ React.createClass的mixins不够自然、直观;React.Component形式非常适合高阶组件(Higher Order Components--HOC),它以更直观的形式展示了比mixins更强大的功能,并且HOC是纯净的JavaScript,不用担心他们会被废弃。HOC可以参考《无状态组件(Stateless Component) 与高阶组件》。

3. React.Component

React.Component是以ES6的形式来创建react的组件的,是React目前极为推荐的创建有状态组件的方式,最终会取代React.createClass形式;相对于 React.createClass可以更好实现代码复用。

class Clock extends React.Component{
  constructor(props){
    // props:调取组件传递进来的属性
    没有执行super()
    console.log(props,this.props) // {title: "今天天气真好"} undefined(如果super(props)会读取到传递的属性
    super(); 
    //SUPER执行,相当于把React.Component当做普通函数执行,让方法中的THIS是当前实例:this=>{props:xxx,constructor:xxx,refs:{},updater:{...}}
  }
  // 必须要有render函数,它返回的内容使我们当前组件要渲染的视图
  render(){
    return <div>
        <h2>{this.props.title}</h2>
    </div>
  }
}

ReactDOM.render(<div>

<Clock title='今天天气真好'></Clock>
</div>, document.getElementById('root'))

React.createClass与React.Component区别

1. React.createClass创建的组件,其每一个成员函数的this都有React自动绑定,任何时候使用,直接使用this.method即可,函数中的this会被正确设置。React.Component创建的组件,其成员函数不会自动绑定this,需要开发者手动绑定,否则this不能获取当前组件实例对象。
2. 组件属性类型propTypes及其默认props属性defaultProps配置不同...

www.cnblogs.com/soyxiaobi/p…

5. react diff 原理(常考,大厂必考)

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

6.为什么建议传递给 setState 的参数是一个 callback 而不是一个对象

因为 this.props 和 this.state 的更新可能是异步的,不能依赖它们的值去计算下一个 state。

7.除了在构造函数中绑定 this,还有其它方式吗

+ 你可以使用属性初始值设定项(property initializers)来正确绑定回调,create-react-app 也是默认支持的。
+ 在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。

8.setState第二个参数的作用

该函数会在setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成:
this.setState(
  { username: 'tylermcginnis33' },
  () => console.log('setState has finished and the component has re-rendered.')
)

9.在构造函数调用 super 并将 props 作为参数传入的作用是啥?

+ SUPER执行,相当于把React.Component当做普通函数执行,让方法中的THIS是当前实例;
+ 在调用 super() 方法之前,子类构造函数无法使用this引用,ES6 子类也是如此。将 props 参数传递给 super() 调用的主要原因是在子构造函数中能够通过this.props来获取传入的

10.简述 flux 思想

Flux将一个应用分成四个部分:
   + View: 视图层
   + Action(动作):视图层发出的消息(比如mouseClick)
   + Dispatcher(派发器):用来接收Actions、执行回调函数
   + Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面

Flux 的最大特点,就是数据的"单向流动":数据总是"单向流动",任何相邻的部分都不会发生数据的"双向流动"。
    + 用户访问 View
    + View 发出用户的 Action
    + Dispatcher 收到 Action,要求 Store 进行相应的更新
    + Store 更新后,发出一个"change"事件
    + View 收到"change"事件后,更新页面

blog.csdn.net/ruike140036…

11.在 React 当中 Element 和 Component 有何区别?

React Element 是描述屏幕上所见内容的数据结构,是对于 UI 的对象表述。典型的 React Element 就是利用 JSX 构建的声明式代码片然后被转化为 createElement 的调用组合。
React Component 是一个函数或一个类,可以接收参数输入,并且返回某个 React Element

12.在 React 中如何处理事件

为了解决跨浏览器的兼容性问题,SyntheticEvent 实例将被传递给你的事件处理函数,它是 React 跨浏览器的浏览器原生事件包装器,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation() 和 preventDefault()。
比较有趣的是,React 实际上并不将事件附加到子节点本身。React 使用单个事件侦听器侦听顶层的所有事件。这对性能有好处,也意味着 React 在更新 DOM 时不需要跟踪事件监听器。

13.createElement 和 cloneElement 有什么区别?

React.createElement():三个参数;
    第一个参数可以是一个标签名。如 divspan,或者 React 组件。
    第二个参数为传入的属性。
    第三个以及之后的参数,皆作为组件的子组件。
React.createElement(
    type,
    [props],
    [...children]
)

React.cloneElement()与 React.createElement()相似,不同的是它传入的第一个参数是一个 React 元素,而不是标签名或组件。

React.createElement(
    element,
    [props],
    [...children]
)

14.如何告诉 React 它应该编译生产环境版本?

通常情况下我们会使用 Webpack 的 DefinePlugin 方法来将 NODE_ENV 变量值设置为 production。编译版本中 React 会忽略 propType 验证以及其他的告警信息,同时还会降低代码库的大小,React 使用了 Uglify 插件来移除生产环境下不必要的注释等信息。

react组件面试题

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

+ 展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。
+ 容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 Flux actions,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。

2.类组件和函数组件之间的区别是啥?

 类组件可以使用其他特性,如状态 state 和生命周期钩子。
 当组件只是接收 props 渲染到页面时,就是无状态组件,就属于函数组件,也被称为哑组件或展示组件。
 函数式组件比类组件性能高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可

** 3.(组件的)状态(state)和属性(props)之间有什么区别?**

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

+ state 是组件自己管理数据,控制自己的状态,可变;
+ props 是外部传入的数据参数,不可变;
+ 没有state的叫做无状态组件,有state的叫做有状态组件;
+ 多用 props,少用 state,也就是多写无状态组件。

4.什么是受控组件?

简单来说,基于状态(或者属性)的更新来驱动视图渲染=>'受控组件'

HTML 中,表单元素如 <input>、<textarea>和<select>通常维护自己的状态,并根据用户输入进行更新。当用户提交表单时,来自上述元素的值将随表单一起发送。
而 React 的工作方式则不同。包含表单的组件将跟踪其状态中的输入值,并在每次回调函数(例如onChange)触发时重新渲染组件,因为状态被更新。以这种方式由 React 控制其值的输入表单元素称为受控组件。

5.什么是高阶组件?

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

HOC 可以用于以下许多用例

+ 代码重用、逻辑和引导抽象
+ 渲染劫持
+ state 抽象和操作
+ props 处理

6.应该在 React 组件的何处发起 Ajax 请求

componentDidMount方法中的代码,是在组件已经完全挂载到网页上才会调用被执行,所以可以保证数据的加载。此外,在这方法中调用setState方法,会触发重渲染。

blog.csdn.net/gao44981298…

7.react中组件传值

1. 基于属性传递props

原理:父级调用子级的时候,在子组件身上绑定一个属性,值为需要传递的数据。子组件中通过 this.props 来获取。

import React, { Component } from 'react'

export default class Child extends Component {
    render() {
        return (
            <div>
                <h2>Child</h2>
                <p>接收到父组件的值为:{this.props.msg}</p>
            </div>
        )
    }
}

export default class Father extends Component {
    constructor() {
        super();
        this.state = {
            msg:"父组件的msg"
        }
    }
    render() {
        return (
            <div className="Father">
                <h2>Father</h2>
                <Child msg={this.state.msg}></Child>
            </div>
        )
    }
}
  • 2.子传父:事件传值

原理: 父组件把自身的方法通过属性传递给子组件,子组件在某些情况下,把传递进来的父组件中的方法执行,把子组件的一些信息传递给方法(底层:还是基于属性)


import React, { Component, Fragment } from 'react'

// 子组件
export default class Child extends Component {
    constructor() {
        super();
        this.state = {
            msg: "子组件的msg"
        }
    }
    render() {
        return (
            <div className="Child">
                <h2>子组件</h2>
                {/* 点击按钮触发父组件传过来的函数 */}
                <button onClick={this.clickHandler.bind(this)}>给父级发送msg</button>
            </div>
        )
    }
    clickHandler() {
        {/* 把需要传递给父组件的值当做参数传进去 */}
        this.props.getMsgHandler(this.state.msg)
    }
    
    // 父组件
    export default class Father extends Component {
    constructor() {
        super();
        this.state = {
            msg: ""
        }
    }
    render() {
        return (
            <div>
                <h2>Father</h2>
                {/* 父组件给子组件传递一个自身的函数,子组件通过调用函数的方式传值给父组件 */}
                <Child getMsgHandler={this.getMsgHandler.bind(this)}></Child>
                <p>接收到父组件的值为:{this.props.msg}</p>
            </div>
        )
    }
    {/* 在传递的方法中,接收数据 */}
    getMsgHandler(val){
        this.setState({
            msg:val
        })
    }
}
  • 3.跨组件传值

原理: 给需要传值的双方绑定同一个事件总线,通过侦听和调用共同的方法,进行传值。

/* 
* 1.$on:把自定义[eventName]的方法[func]放在一个事件池的数组中
* 2.$emit:调用时,循环存储在事件池的方法执行
*/

class VoteMain extends React.Component {
	state = {
		supNum: 0,
		oppNum: 0
	};
	handle = type => {
		let { supNum, oppNum } = this.state;
		type === 'SUP' ? this.setState({ supNum: supNum + 1 }) : this.setState({ oppNum: oppNum + 1 });
	};
	render() {
		let { supNum, oppNum } = this.state;
		return <main className="mainBox">
			<p>支持人数:{supNum}</p>
			<p>反对人数:{oppNum}</p>
		</main>;
	}
	componentDidMount() {
		// 订阅自定义事件方法
		EM.$on('mainHandle', this.handle);
	}
}

class VoteFooter extends React.Component {
	render() {
		return <footer className="footerBox">
			<button onClick={ev => {
				// 通知订阅的方法执行
				EM.$emit('mainHandle', 'SUP');
				EM.$emit('indexHandle');
			}}>支持</button>
			<button onClick={ev => {
				EM.$emit('mainHandle', 'OPP');
				EM.$emit('indexHandle');
			}}>反对</button>
		</footer>;
	}
}

export default class Vote extends React.Component {
	state = { total: 0 };
	render() {
		return <div className="voteBox">
			<header className="headerBox">
				<h3>{this.props.title}</h3>
				<span>N:{this.state.total}</span>
			</header>
			<VoteMain></VoteMain>
			<VoteFooter></VoteFooter>
		</div>;
	}
	componentDidMount() {
		EM.$on('indexHandle', () => {
			this.setState({
				total: this.state.total + 1
			});
		});
	}
}

EventEmit方法封装

/* EventEmit: 自定义发布订阅改变父子,兄弟之间的信息通信
* 1.$on:把自定义[eventName]的方法[func]放在一个事件池的数组中
		1. 判断是否私有,非私有的,创建一个初始值[]
		2. 订阅方法去重
* 2.$emit:调用时,循环存储在事件池的方法执行
*/

class EventEmit {
	//=>事件池 {事件名:[订阅方法,...]}
	pond = {};
	$on(eventName, func) {
		// 如果没有此事件,我们初始化创建一个值
		if (!this.pond.hasOwnProperty(eventName)) {
			this.pond[eventName] = [];
		}
		// 订阅方法的去重
		if (this.pond[eventName].some(item => item === func)) return;
		this.pond[eventName].push(func);
	}
	$emit(eventName, ...args) {
		let arr = this.pond[eventName] || [];
		arr.forEach(item => {
			item.call(null, ...args);
		});
	}
}

export default new EventEmit();
  • 4.执行上下文进行信息传递

ThemeContext.Provider

1.创建一个上下文对象
    let ThemeContext = React.createContext();
2.【ThemeContext.Provider】祖先组件注册上下文中的内容
    1.把父元素用<ThemeContext.Provider></ThemeContext.Provider>包裹起来
    2.基于value向组件元素的上下文中设置内容
3.后代组件进行消费(获取到上下文中的内容)
    1.在需要使用的子元素中设置static contextType = ThemeContext(提供的内容放到this.context中了)
直接{this.context.xxx}
    2.需要把子组件用ThemeContext.Consumer包裹起来,子元素内容用函数包裹起来,context就是祖先中注册的上下文信息 context===value
import React from 'react';

let ThemeContext = React.createContext();
class CountShow extends React.Component {
    // 2. 后代消费(第一种)
    static contextType = ThemeContext;
    render() {
        return <div>{this.context.num}</div>
    }
}

class CountButton extends React.Component {
    static contextType = ThemeContext;
    render() {
        return <ThemeContext.Consumer>
            {/* 第二种消费方式:ThemeContext.Consumer 需要把子元素内容用函数包裹起来
            context就是祖先中注册的上下文信息 context===value*/}
            {value => {
                return <button onClick={
                    ev => {
                        value.handle()
                    }
                }>累加</button>
            }}
        </ThemeContext.Consumer>
    }
}
class Count extends React.Component {
    state = {
        num: 0,
        handle: this.handle
    }
    handle = () => {
        this.setState({
            num: this.state.num + 1
        })
    }
    render() {
        // 1.基于value 向祖先元素的上下文设置内容
        return <ThemeContext.Provider value={{ num: this.state.num, handle: this.handle }}>
            <h3>计数器</h3>
            <CountShow></CountShow>
            <CountButton></CountButton>
        </ThemeContext.Provider>
    }
}

export default Count;

8.什么时候在功能组件( Class Component )上使用类组件( Functional Component )?

9.受控组件和非受控组件区别是什么?

受控组件:即组件负责控制和管理自己的状态(任何改变代用setSate处理)
不受控组件:组件数据不全部是setState来处理,还有DOM交互,比如refs来操控真实DOM

虽然不受控制的组件通常更容易实现,因为您只需使用引用从DOM获取值,但是通常建议您通过不受控制的组件来支持受控组件。
主要原因是受控组件支持即时字段验证,允许您有条件地禁用/启用按钮,强制输入格式。

10.react 组件的划分业务组件技术组件?

生命周期

1.React 组件生命周期有哪些不同阶段?

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

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

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

2.react 生命周期函数

初始化阶段:getDefaultProps==> constructor ==> componentWillMount ==> render ==> componentDidMount

运行中状态:componentWillReceiveProps=>shouldComponentUpdate==>componentWillUpdate==> render==>componentDidUpdate
·
销毁阶段:componentWillUnmount
生命周期的顺序:defaultProps==> constructor ==> componentWillMount ==> render ==> componentDidMount==> componentWillUnmount

getdefaultProps 处理属性(获取默认值和校验传递属性类型) 调用1次
getInitialState 处理(执行)状态constructor,创建实例,设置初始状态; 调用1次
componentWillMount: 第一次组件渲染之前(把获取的数据重新赋值给状态或者存放到REDUX中)  调用1次
render:第一次或者重新进行视图的渲染  调用>=1次
componentDidMount:第一次渲染完成(此处可以获取到DOM元素了)  仅客户端,调用1次
componentWillReceiveProps(nextProps) {函数在组件接收到一个新的 prop在初始化render的时候不会执行,它会在组件接受到新的状态(Props)时被触发,一般用于父组件状态更新时子组件的重新渲染 >=0次 
shouldComponentUpdate(nextProps,nextState):确定是否更新组件。默认情况下,它返回true。如果确定在 state 或 props 更新后组件不需要在重新渲染,则可以返回false,这是一个提高性能的方法。  调用>=0次
componentWillUpdate  组件开始重新渲染之前调用。 调用>=0次
componentDidUpdate  组件重新渲染并且把更改变更到真实的 DOM 以后调用  调用>=0次
componentWillUnmount 在组件从 DOM 中移除之前立刻被调用。它用于取消任何的网络请求,或删除与组件关联的所有事件监听器。 调用1

2.react生命周期中,最适合与服务端进行数据交互的是哪个函数

componentDidMount:在这个阶段,实例和dom已经挂载完成,可以进行相关的dom操作。

react性能比较面试题

1.vue和react的区别

Vue最大的优势,就是实现了数据的双向绑定,而React的数据流动是单向的。
React中是把html和css全都写进js中。而Vue采用的是模板,就是在html中写css和js,最后再用webpack和vue-loader进行打包,这种编码方式对于初学者而言是很舒服的
在React中要想更新状态,必须调用setState方法,而在Vue中只需要通过this的某种方式去更新state中的数据,这种方式更加方便
Vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树
React严格上只针对MVC的view层,Vue是MVVM模式

2.react性能优化的方案

3.React 项目用过什么脚手架

redux面试题

1.redux中间件
2.redux有什么缺点
3.了解 redux 么,说一下 redux 把

4.介绍一下webpack

webpack是一个前端模块化打包工具,主要由入口,出口,loader,plugins四个部分。前端的打包工具还有一个gulp,不过gulp侧重于前端开发的过程,而webpack侧重于模块,例如他会将css文件看作一个模块,通过css-loader将css打包成符合css的静态资源。

5.如果你创建了类似于下面的 Twitter 元素,那么它相关的类定义是啥样子的?

6.为什么我们需要使用 React 提供的 Children API 而不是 JavaScript 的 map?