【前端面试】React

React

React事件机制

React(JSX)并不是将事件绑定到了真实DOM上,而是在document处监听了所有的事件,当事件发生并且冒泡到document的时候,React将事件内容封装并交由真正的处理函数进行处理。这种方式不仅减少了内存的消耗,还能在组件挂载销毁时统一进行事件的订阅和移除。

除此之外,冒泡到document上的事件也不是原生的浏览器事件,而是由react实现的而合成事件(SyntheticEvent)。如果不需要事件冒泡,应该下龙event.stopPropagation()方法,而不是调用event.preventDefault()方法。
在这里插入图片描述
实现合成事件的目的

  • 浏览器兼容

    合成事件首先解决了浏览器之间的兼容问题,另外,合成事件是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力。

  • 事件对象统一管理

    对于原生浏览器事件来说,浏览器会给监听器创建一个事件对象,如果有许多事件监听,就需要分配很多的事件对象,造成高额的内存分配问题。
    但是对于合成事件来说,有一个事件池来专门管理事件对象的创建和销毁,当事件被使用时,就会从事件池中复用对象,事件回调结束后,销毁事件对象上的属性,为下次复用事件对象做准备。

生命周期

React生命周期

以React16.0以后的生命周期为例:
在这里插入图片描述
React通常将组件的生命周期分为以下三个阶段:

  • Mount,挂载阶段:组件第一次在DOM树中被渲染的过程
  • Update,更新阶段:组件状态发生变化,重新更新渲染的过程
  • Unmount,卸载阶段:组件从DOM树中移除的过程
挂载阶段

该阶段组件被创建,然后组件实例插入到DOM树中,完成组件的第一次渲染。这个过程只会发生一次。

依次调用的方法:

  • constructor
  • getDerivedStateFromProps
  • render
  • componentDidMount
  1. constructor

    组件的构造函数,第一个被执行。
    若不显式定义,会有默认的构造函数;
    若显式定义,必须在构造函数中执行super(props),否则无法在构造函数中拿到this。

    如果不需要初始化state或者不需要绑定方法,就不需要实现React组件的constructor。

    constructor中一般只做两件事情:

    • 初始化组件的state
    • 为事件处理方法绑定this
    constructor(props) {
    	super(props);
    	// 直接给state设置初始值,不要在构造函数中调用setState
    	this.state = { counter: 0 };
    	this.handler = this.handler.bind(this);
    }
    
  2. getDerivedStateFromProps

    static getDerivedStateFromProps(props, state)
    

    静态方法,无法在函数中使用this,有两个参数props和state,该函数返回一个对象用来更新state对象,若不需要更新state,可以返回null。

    getDerivedStateFromProps函数在挂载阶段,当接收到新的props或者调用setState、forceUpdate时被调用。如果接收到新的属性,需要更新state时可以使用该函数。

    // 当props.msg改变,更新state
    class App extends React.Component {
    	constructor(props) {
    		super(props);
    		this.state = {
    			msg: 'init'
    		}
    	}
    
    	static getDerivedStateFromProps(props, state) {
    		if (props.msg !== state.msg) {
    			return { msg: props.msg }
    		}
    		return null;
    	}
    
    	handler = () => {
    		this.setState({
    			msg: 'update'
    		});
    	}
    
    	render() {
    		return (
    			<div onClick={this.handler}>{this.state.msg}</div>
    		)
    	}
    }
    

    注意:React16.4^的版本,setState和forceUpdate也会触发getDerivedStateFromProps这个生命周期。所以更新时可能会出现更新不正确的问题,需要注意。

  3. render

    React最核心的方法,组件中必须有的方法。
    render函数根据props和state渲染组件,只做一件事,就是返回需要渲染的内容,不要在render函数内做其他业务逻辑。

    函数返回类型:

    • React元素:原生DOM、React组件
    • 数组和Fragment(片段):可以返回多个元素
    • Portals(插槽):可以将子元素渲染到不同的DOM子树中
    • 字符串和数组:渲染成DOM中的text节点
    • 布尔值或null:不渲染任何内容
  4. componentDidMount

    componentDidMount在组件挂载(插入DOM树)后立即调用。

    该阶段可以进行的操作:

    • 执行依赖于DOM的操作
    • 发送网络请求
    • 添加订阅消息(取消订阅可以放在componentWillUnmount中)

    如果在component中调用setState,会触发一次额外的渲染(多调用一次render函数)。由于这次额外渲染是在浏览器刷新屏幕前执行,用户是无感知的,但是应当避免这样使用,会降低性能,尽量在constructor中进行state的初始化。

    class App extends React.Component {
    	constructor(props) {
    		super(props);
    		this.state = {
    			msg: 'mounting'
    		}
    	}
    
    	componentDidMount() {
    		this.setState = {
    			msg: 'mounted'
    		}
    	}
    
    	render() {
    		return (
    			<div>状态:{ this.state.msg }</div>
    		)
    	}
    }
    
更新阶段

当组件的props发生改变,或组件内调用setState/forceUpdate,就会触发重新渲染,这个过程可能发生多次。

当前阶段会依次调用下述方法:

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate
  1. shouldComponentUpdate

    shouldComponentUpdate(nextProps, nextState)
    

    需要注意下边两个组件更新的问题:

    • 组件内setState的值即使与原来state中的值相同,也会引起组件重新渲染
    • 如果父组件重新渲染,不管传入子组件中的props是否变化,都会引起子组件的重新渲染

    可以用shouldComponentUpdate解决上述两个问题,提升性能。该方法在组件重新渲染前触发,默认返回true,可以根据比较this.props和nextProps以及this.state和nextState,来返回true或false来控制组件是否更新。如果返回false,组件停止更新过程,那么后续的render和componentDidUpdate不会被调用。

  2. getSnapshotBeforeUpdate

    getSnapshotBeforeUpdate(prevProps, prevState) {
    	return {}
    	// return 必须,默认return null
    	// 返回值作为第三个参数传给componentDidUpdate
    }
    prevProps:更新之前的props
    prevState:更新之前的state
    

    getSnapshotBeforeUpdate方法在render之后、componentDidUpdate之前调用,这个函数必须要和componentDidUpdate一起使用。

  3. componentDidUpdate

    componentDidUpdate会在更新后立即调用,首次渲染(挂载阶段)不会执行此方法。

    componentDidUpdate(prevProps, prevState, snapshot) {}
    prevProps: 更新之前的props
    prevState: 更新之前的state
    snapshot: getSnapshotBeforeUpdate()生命周期的返回值
    

    在该阶段可以执行的操作:

    组件更新后,对DOM进行操作
    如果对更新前后的props进行比较,可以在这个方法内进行网络请求,不改变,不请求。

卸载阶段

只有componentWillUnmount这一个生命周期函数,在组件卸载、销毁之前调用,在这个函数中使用setState是无用的,因为组件被卸载就不会重新渲染。

可以在这个函数中进行清理操作:

  • 消除定时器、取消或消除网络请求
  • 取消在componentDidMount()中创建的订阅
错误处理阶段

在后代组件抛出错误后,调用componentDidCatch方法

componentDidCatch(error, info)
error: 抛出的错误
info: 带有componentStack key的对象,其中包含有关组件引发错误的栈信息

React常见生命周期过程

在这里插入图片描述

过程:

  • 挂载阶段,首先执行constructor构造方法,创建组件
  • 创建完成后,执行render方法,返回需要渲染的内容
  • React将需要渲染的内容挂载到DOM树上
  • 挂载完成后,执行componentDidMount生命周期函数
  • 给组件创建一个props(组件通信)、调用setState、调用forceUpdate(强制更新),都会重新调用render函数
  • render函数重新执行之后,重新进行DOM树的挂载
  • 挂载完成后,执行componentDidUpdate
  • 移除组件后,执行componentWillUnmount

总结:

  • getDefaultProps

    初始化组件的props,仅在组件创建之前被调用一次。

  • getInitialState

    初始化组件的state值

  • componentWillMount
    组件创建后、render前调用。React官方不推荐,React16废弃。

  • render
    唯一一个必须要实现的方法。
    一般需要返回jsx元素,React根据props和state把组件渲染出来,如果不需要渲染内容,返回null或false

  • componentDidMount

    组件挂载后立即调用,标志组件挂载完成。
    需要获取DOM节点信息的操作可以在这个阶段完成。
    也可以发起ajax请求,React官方推荐的请求时机。
    与componentWillMount一样,仅调用一次。

React16中的新生命周期总结

在这里插入图片描述
React16对生命周期自上而下地做了另一维度的解读:

  • render阶段

    用于计算一些必要的状态信息。这个阶段可能会被React暂停(react 16 引入fiber)

  • Pre-commit阶段

    commit指的是更新真正的DOM节点。
    Pre-commit阶段,是指还没有真正更新真实的DOM,但是DOM信息可以被读取了。

  • commit阶段

    该阶段,React会完成真实DOM的更新工作。commit阶段,可以拿到真实的DOM、refs

流程方面还是按照挂载、更新、卸载进行划分

挂载过程

  • constructor
  • getDerivedStateFromProps
  • render
  • componentDidMount

更新过程

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • getSnapshotBeforeUpdate
  • componentDidUpdate

卸载过程

  • componentWillUnmount

React废弃的生命周期

fiber出现之后,废弃了render之前调用的三个函数:componentWillMount、componentWillReceiveProps、componentWillUpdate。
这三个函数,可能会因为高优先级任务的出现被打断而导致多次执行。 另一个原因可能是React想约束使用者,好的框架能够让人写处易维护、易扩展的代码,而新增和将废弃的生命周期入手分析代码,能够提升这一点。

componentWillMount

可以用componentDidMount和constructor来代替。

componentWillReceiveProps

老版本React中,如果state与porps密切相关,需要在componentWillReceiveProps中判断前后props是否相同,并触发state的更新。但是这样做会破坏state数据源的单一性。并且会增加组件的重绘次数。

React引入一个新的生命周期函数getDerivedStateFromPorps。

getDerivedStateFromPorps优点:

  • 静态函数,纯函数,不能使用this,无副作用
  • 开发者只能通过prevState而不是prevProps来对比,保证了state和props之间的而简单关系,也不要处理第一次渲染时prevProps为空的情况

componentWillUpdate
与componentWillReceiveProps类似,在componentWillUpdate根据props的变化触发一些回调,但是无论是componentWillReceiveProps还是componentWillUpdate,在一次更新中都可能会被调用多次。

将原来卸载componentWillUpdate中的回调迁移至componentDidUpdate可以解决上述问题。

还有一种情况,如果需要获取DOM元素的状态,但是由于fiber中,render可以被打断,在componentWillUpdate中获取到的状态很可能与真正的状态不同。这个可以使用新增的生命函数getSnapshotBeforeUpdate去解决

React 16.x中props改变后调用的生命周期

getDerivedStateFromProps
替代了被废弃的componentWillReceiveProps
该函数是静态函数,无法通过this访问class的属性
通过nextProps以及prevState来进行判断,根据新传入的props来选择是否更新state

static getDerivedStateFromProps(nextProps, prevState) {
	const { msg } = nextProps;
	// 当传入的msg改变,更新state
	if (msg !== prevState.msg) {
		return { msg };
	}

	return null; // 不需要更新state,返回null
}

React性能优化在哪个生命周期?优化原理?

react父组件render函数重新渲染会引起子组件render方法的重新渲染。但是如果子组件接受父组件的数据没有改变,这是子组件render就是无用的,影响性能的。

上述问题可以在shouldComponentUpdate中解决

shouldComponentUpdate(nextProps) {
	if (this.props.msg === nextProps.msg) {
		return false;
	}
	return true;
}

但是对于引用数据类型,如果数据引用地址没变,哪怕内容改变了,会被判定为false,不进行更新;或者数据引用地址改变,内容没有改变,会被判定为true,进行更新。

解决方法:

  1. setState改变数据前,采用assgin进行拷贝,但是因为是浅拷贝,不完美 (与扩展运算符一样的效果)
const o = Object.assign({}, this.state.obj);
o.student.count = '111';
this.setState({ obj: o });
  1. 使用JSON方法进行深拷贝,但是遇到数据为undefined和函数时会报错

state、setState、props

调用原理

在这里插入图片描述
执行过程:

  1. 调用setState入口函数,入口函数充当的是分发器的角色,根据入参不同,分发到不同的功能函数中去
ReactComponent.prototype.setState = function (partialState, callback) {
	this.updater.enqueueSetState(this, partialState);
	if (callback) {
		this.updater.enqueueCallback(this, callback, 'setState');
	}
}
  1. enqueueSetState方法将新的state放入组件的状态队列里,并调用enqueueUpdate来处理将要更新的实例对象。
enqueueSetState: function(publickInstance, partialState) {
	// 通过this拿到组件实例
	const internalInstance = getInternalInstanceReadyForUpdate(publickInstance, 'setState');
	// 这个queue对应的就是一个组件实例的state数组
	const queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
	queue.push(partialState);
	// enqueueUpdate用来处理当前的组件实例
	enqueueUpdate(internalInstance);
}
  1. 在enqueueUpdate方法中,引出一个关键的对象——batchingStrategy,该对象所具备的isBatchingUpdates属性直接决定了当下是要走更新流程(false)还是排队等待(true)。
    如果轮到执行更新,就调用batchedUpdates方法来直接发起更新流程。
    batchingStrategy或许是React内部专门用于管控批量更新的对象。
function enqueueUpdate(component) {
	ensureInjected();
	// 关键判断
	if (!batchingStrategy.isBatchingUpdates) { // 若当前没有处于批量创建/更新组件的阶段,立即更新
		batchingStrategy.batchedUpdates(enqueueUpdate, component);
		return;
	}

	// 否则组件塞入dirtyComponents队列里,排队等待
	dirtyComponents.push(component);
	if (component._updateBatchNumber == null) {
		component._updateBatchNumber = updateBatchNumber + 1;
	}
}

batchingStrategy对象可以理解为锁管理器。
锁是指React全局唯一的isBatchingUpdates变量,isBatchingUpdates初始值为false,代表当前未进行任何批量更新操作。
当React调用batchedUpdate去执行更新动作时,先将锁给锁上,也就是isBatchingUpdates置为true,表示当前正在进行批量更新。当isBatchingUpdates为true(锁被锁上)时,任何需要更新的组件只能暂时进入dirtyComponents队列中进行排队(等待下一次的批量更新,并且不能随意插队)。

任务锁的思想,是React面对大量状态仍然能够实现有序分批处理的基石。

setState函数调用之后发生了什么

调用setState函数后,React将传入的参数对象与组件当前的状态合并,然后触发调和过程(Reconciliation)。
经过调和过程,React会以相对高效的方式根据新的状态构建React元素树,并且着手重新渲染整个UI界面。

React得到新构建的元素树后,会自动计算出新元素树与老元素树的节点差异,然后根据差异对界面进行最小程度的渲染。在差异计算算法中,React能够相对精确的知道发生改变的位置以及如何改变,这样就保证了按需更新,而不是全部重新渲染。

如果短时间内频繁的setState。React将state的改变压入栈中,在合适的时机,批量更新state和试图,达到性能优化。

setState批量更新的过程

调用setState,组件的state不会立即改变,setState是把需要修改的state放入一个等待更新的队列中。React对真正的执行时机进行优化,为了提高性能,React事件处理程序中如果有多次setState,那么setState的状态将被修改合并成一次状态修改,这样最终更新只产生一次组件及其子组件的重新渲染。

this.setState({
	count: this.state.count + 1 // 入队,[count+1]
});
this.setState({
	count: this.state.count + 1 // 入队,[count+1, count+1]
});

// 上面两次setState将合并,但是例如上述多次+1,最终只有一次生效,因为在同一个方法中多的setState的合并动作不是单纯地将更新累加。例如相同属性的设置,React只保留最后一次更新

setState是同步还是异步

setState并不是单纯的同步或者异步操作,它的表现由调用场景决定。
源码中,通过isBatchingUpdates来判断setState是直接更新还是存入state队列中等待(true等待执行异步操作,false直接更新)。

  • 异步

    React可以控制的地方,比如React生命周期事件和合成事件中,都会走合并操作,延迟更新的策略。

  • 同步

    在React无法控制的地方,比如原生事件,例如addEventListener、setTimeout、setInterval等事件中,只能同步更新。

React中setState异步设计?

  • 提升性能

    假如每次调用setState都进行更新,那么意味着render函数会频繁的调用,界面重新进行渲染,这样效率非常低。
    而异步设计,可以让React获取到多个更新操作,然后进行批量更新。

  • 保持state和props的一致性

    如果同步更新了state,但是还没有执行render函数,这样state和props就无法保证同步,如果无法保证state和props的一致性,那么在开发过程中会产生很多问题。

setState的第二个参数的作用

setState的第二个参数是可选的回调函数。

回调函数在组件重新渲染后执行,相当于在componentDidUpdate生命周期内执行(通常建议用componentDidUpdate代替使用这个回调函数)。

在回调函数中,可以拿到更新后的state值。

this.setState({ 
	xx: 'xxx' 
}, newState => {
	console.log(newState); // 更新完成后的回调函数,函数中可以拿到更新后的state
});

setState和replaceState的区别

  • setState

    用于设置状态对象,是React事件处理函数中和请求回调函数中触发UI更新的主要方法。

    setState(nextState[, callback]);
    nextState: 将要设置的新状态,该状态会和当前的state合并
    callback: 可选,更新后的回调函数,在组件重新渲染后调用
    
  • replaceState

    与setState方法类似,但是只保留nextState中的状态。
    原来state中如果存在nextState中不存在的状态,会被删除。

    replaceState(nextState[, callback]);
    nextState:将要设置的新状态,该状态会替换当前state
    callback:可选,更新后的回调函数,在组件重新渲染后调用
    

区别:
setState是修改state中的部分状态,而replaceState是直接替换原来的state,如果新状态中的属性减少,state中这个属性将不再存在。

React类组件中的this.state和this.setState的区别

this.state通常是用来初始化state的,而this.setState是用来修改state值的。
如果初始化state之后再使用this.state进行赋值操作,之前的state将会被覆盖掉,如果使用this.setState则会替换掉相应的state值。

如果需要修改state的值,使用this.setState。
如果需要替换state的值,那么直接给this.state赋值。

state如何注入组件,从reducer到组件的过程

props和state的区别

  • props

    props是一个从外部传进组件的参数,主要作用是从父组件向子组件传递数据。
    具有可读性和不变性,只能通过外部组件主动传入新的props来重新渲染子组件,否则子组件的props以及展现形式不会改变。

  • state

    state的主要作用是,组件维护自身的状态,类组件中只能在constructor中初始化,是组件的私有属性,无法在外部访问和修改,只能在组件内部通过this.setState来修改。
    state发生改变,可能会导致组件的重新渲染。

区别:

  • props是父到子组件中进行传递的,而state是组件内用于管理状态的。
  • props不可修改;state可以修改,setState操作后会异步更新。

props为什么只读

this.props是组件之间沟通的一个接口,原则上,只能从父组件流向子组件。
this.props汲取了纯函数的思想。props的不可变性保证了相同的输入,页面显示的内容是一样的,并且不会有副作用。

纯函数特点:

  • 给定相同的输入,输出总是相同
  • 没有副作用
  • 不依赖外部状态

React中组件props改变时,更新组件的方法

在组件传入的props更新时,重新渲染该组件常用的方法是在componentWillReceiveProps将新的props更新到组件的state中(这种state称为派生状态),从而实现重新渲染。

在React 16.3中,引入一个新的钩子函数getDerivedStateFromProps

  1. componentWillReceiveProps(已废弃)

    在componentWillReceiveProps生命周期中,可以在子组件的render函数执行前,通过this.props获取旧的属性,通过nextProps获取新的props,对比两次props是否相同,来更新子组件自己的state。

    好处:

    可以将数据请求放在这个生命周期中执行,需要父组件传递的参数,从componentWillReceiveProps(nextProps)中获取。不必将所有的请求都放在父组件中,该请求只会在组件渲染时才会发出,从而减少请求次数。

  2. getDerivedStateFromProps(16.3引入)

    替代componentWillReceiveProps,要使用componentWillReceiveProps的场景,可以考虑使用getDerivedStateFromProps。

    getDerivedStateFromProps是一个静态函数,这个函数不能通过this访问到class的属性(本身也不推荐直接访问属性)。getDerivedStateFromProps提供nextProps以及prevState参数,通过这两个参数,可以进行判断,是否根据新传入的props来同步到state。

    如果props传入的内容不需要同步到state中,那么需要返回一个null,并且返回值是必须的。

    static getDerivedStateFromProps(nextProps, prevState) {
    	const { msg } = nextProps;
    	// 当传入的msg发生变化时,更新state
    	if (msg !== prevState.msg) {
    		return { msg };
    	}
    	// 否则,对于state不进行任何操作
    	return null;
    }
    

如何检验props,以及验证的目的

React中提供了PropTypes以供验证使用。当Props传入的数据无效(传入的数据类型和验证的数据类型不符),会在控制台发出警告信息。
验证的目的:可以避免随着应用越来越复杂时,数据类型不一致带来的问题;可以提升代码的可读性。

import PropTypes from 'prop-types';

class Test extends React.Component {
	render() {
		return (
			<p>Test msg, {this.props.msg}</p>
		);
	}
}

Test.propTypes = {
	msg: PropTypes.string
}

React中使用getDefaultProps的作用

通过实现组件中的getDefaultProps方法,为属性设置默认值。

var ShowTitle = React.createClass({
	getDefaultProps: function() {
		return {
			title: 'React'
		}
	},
	render: function() {
		return <h1>{this.props.title}</h1>
	}
});

React 组件通信

父子组件间通讯方式

父向子组件通信

通过props向子组件传递需要的信息

// 父组件
const Parent = () => {
	return <Child msg="parent"/>;
}
// 子组件
const Child = props => {
	return <p>{props.msg}</p>
}

子向父组件通信

props+回调函数的方式

// 父组件
const Parent = () => {
	const cb = info => {
		console.log('来自子组件的信息');
	}
	return <Child msg="parent" cb={cb} />
}
// 子组件
const Child = props => {
	const { cb, msg } = props;
	const handleClick = info => {
		cb('子组件信息:' + info);
	}

	return <button onClick={msg => handleClick(msg + '已点击')}>click: {msg}</button>
}

跨级组件的通讯方式

组件向其孙子组件或更深层子组件通信

  • 使用props

    利用中间件层层传递,但是层级过深,每一个中间层都需要去传递props,增加了复杂度,而且这些props并不是中间组件自己需要的。

  • 使用context

    context相当于一个大容器,可以把要通信的内容放在这个容器中,这样不管嵌套多深,都可以随意去用,对于跨级多层的全局数据可以使用context实现。

    const BatteryContext = createContext();
    // 父组件
    class Parent extends React.Component {
    	state = {
    		color: 'red'
    	}
    	render() {
    		const { color } = this.state;
    		return (
    			<BatteryContext.Provider value={color}>
    				<Child></Child>
    			</BatteryContext>
    		);
    	}
    }
    // 子组件
    const Child = () => <GrandChild />
    // 孙子组件
    class GrandChild extends React.Component {
    	render() {
    		return (
    			<BatteryContext.Cunsumer>
    				{color => <h1 style={{ color: color }}>{color}</h1>}
    			</BatteryContext.Cunsumer>
    		)
    	}
    }
    

非嵌套关系组件间通信

即没有任何包含关系的组件,包括兄弟组件已经不在同一个父级中的非兄弟组件。

  • 自定义事件通信(发布订阅模式)
// 接收
class ComponentA extends React.Component {
    componentDidMount() {
        document.addEventListener('myEvent', this.handleEvent)
    }
    componentWillUnmount() {
        document.removeEventListener('myEvent', this.handleEvent)
    }
    
    handleEvent = (e) => {
        console.log(e.detail.log)  //i'm zach
    }
}

// 发布
class ComponentB extends React.Component {
    sendEvent = () => {
        document.dispatchEvent(new CustomEvent('myEvent', {
          detail: {
             log: "i'm zach"
          }
        }))
    }
    
    render() {
        return <button onClick={this.sendEvent}>Send</button>
    }
}

改进:上边代码中事件都绑定在document上,可能会有冲突。创建一个EventBus,专门用于处理这种事件

class EventBus {
    constructor() {
        this.bus = document.createElement('eventBus');
    }

    addEventListener(event, callback) {
        this.bus.addEventListener(event, callback);
    }

    removeEventListener(event, callback) {
        this.bus.removeEventListener(event, callback);
    }

    dispatchEvent(event, detail = {}) {
        this.bus.dispatchEvent(new CustomEvent(event, { detail }));
    }
}

export default new EventBus;

import EventBus from './EventBus';
class ComponentA extends React.Component {
    componentDidMount() {
        EventBus.addEventListener('myEvent', this.handleEvent);
    }
    componentWillUnmount() {
        EventBus.removeEventListener('myEvent', this.handleEvent);
    }
    
    handleEvent = (e) => {
        console.log(e.detail.log);  //i'm zach
    }
}

class ComponentB extends React.Component {
    sendEvent = () => {
        EventBus.dispatchEvent('myEvent', {log: "i'm zach"}));
    }
    
    render() {
        return <button onClick={this.sendEvent}>Send</button>
    }
}
  • 通过redux等进行全局状态管理
  • 如果是兄弟组件通信,可以找到这两个兄弟节点共同的父节点,结合父子间通信方式进行通信。

解决props嵌套层级过深问题

  • 使用context
  • 使用redux

React Hook

虚拟dom

为什么要用VirtualDom

Redux

Mobx

React源码

React面试题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
React 是一个开源前端 JavaScript 库,用于构建用户界面,尤其是单页应用程序。它的主要特点包括使用 Virtual DOM 而不是真实的 DOM 来降低真实的 DOM 操作成本,支持服务端渲染,遵循单向数据流或数据绑定,以及使用可重用/可组合的 UI 组件开发视图。 在 React 中创建组件有两种方法:函数组件和类组件。函数组件是最简单的方式,它是纯 JavaScript 函数,接受 props 对象作为第一个参数并返回 React 元素。类组件则是使用 ES6 类来定义,它继承自 React.Component,并实现 render 方法来返回 React 元素。根据实际需求,可以选择使用函数组件或类组件。 React 的优点包括使用 Virtual DOM 提高应用程序性能,易于读写的 JSX 语法,支持客户端和服务端渲染,易于与其他框架集成,以及方便编写单元与集成测试。然而,React 也有一些局限性,例如它只是一个视图库而不是完整的框架,对于初学者来说有一定的学习曲线,需要额外的配置来集成到传统的 MVC 框架中,以及代码复杂性随着使用内联模板和 JSX 增加而增加。 希望这些信息可以帮助你在前端面试中了解 React。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [react前端面试题整合](https://blog.csdn.net/zerogf/article/details/105554272)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值