React
记录一些我见到的react面试题
react合成事件
合成事件(SyntheticEvent),是对浏览器原生事件对象的一层封装,兼容主流浏览器,同时拥有与浏览器原生事件相同的API,如stopPropagation和preventDefault
SyntheticEvent存在的目的是消除不同浏览器在事件对象间的差异,换句话说,抹平了浏览器兼容性差异
react16和react17合成事件和原生事件的执行顺序问题
我们有如下jsx,具体demo
<div
ref={divDom}
className="App1"
onClickCapture={(e) => {
e.stopPropagation();
logFunc("div", true, true);
}}
onClick={(e) => {
logFunc("div", true);
}}
>
<h1
ref={h1Dom}
onClickCapture={() => logFunc("h1", true, true)}
onClick={(e) => {
logFunc("h1", true);
}}
>
Hello CodeSandbox
</h1>
</div>react16执行顺序
可以看到,react16的执行顺序是:
- 原生,捕获,document
- 原生,捕获,div
- 原生,捕获,h1
- 原生,冒泡,h1
- 原生,冒泡,div
- 合成,捕获,div
- 合成,捕获,h1
- 合成,冒泡,h1
- 合成,冒泡,div
- 原生,冒泡,document
流程图总结
当原生事件执行到document的冒泡阶段时,react在这个阶段开始执行合成事件,当合成事件流执行完成后,会执行document的冒泡阶段
题外话
stopPropagation:阻止捕获和冒泡阶段中当前事件的进一步传播
摘自MDN
Event 接口的 stopPropagation() 方法阻止捕获和冒泡阶段中当前事件的进一步传播。但是,它不能防止任何默认行为的发生;例如,对链接的点击仍会被处理。如果要停止这些行为,请参见 preventDefault() 方法,它可以阻止事件触发后默认动作的发生。它也不能阻止附加到相同元素的相同事件类型的其他事件处理器,如果要阻止这些处理器的运行,请参见 stopImmediatePropagation() 方法。
那么,当我们给div加上stopPropagation之后,就只会执行原生document的捕获事件和原生div的捕获事件
const divClickCapFunc = (e) => {
e.stopPropagation(); // 这里阻止捕获进行下去
logFunc("div", false, true);
};当我们给div的合成事件的事件流中断
<div
ref={divDom}
className="App1"
onClickCapture={(e) => {
e.stopPropagation(); // 这里加上stop
logFunc("div", true, true);
}}
onClick={(e) => {
logFunc("div", true);
}}
>
<h1
ref={h1Dom}
onClickCapture={() => logFunc("h1", true, true)}
onClick={(e) => {
logFunc("h1", true);
}}
>
Hello CodeSandbox
</h1>
</div>执行顺序为:
- 原生,捕获,document
- 原生,捕获,div
- 原生,捕获,h1
- 原生,冒泡,h1
- 原生,冒泡,div
- 合成,捕获,div
- 原生,冒泡,document
原生事件不变,合成事件只执行到了div的捕获
react17执行顺序
把上面的demo改成17就能看到效果了
执行顺序为:
- 原生,捕获,document
- 合成,捕获,div
- 合成,捕获,h1
- 原生,捕获,div
- 原生,捕获,h1
- 原生,冒泡,h1
- 原生,冒泡,div
- 合成,冒泡,h1
- 合成,冒泡,div
- 原生,冒泡,document
可以看到,区别就是,合成事件的捕获时机提前了,变成在document的捕获时机之后,原生捕获时机之前
当我们给div加上stopPropagation之后,由于合成事件的捕获时机提前,所以也能够执行合成事件的捕获时机
const divClickCapFunc = (e) => {
e.stopPropagation(); // 这里阻止捕获进行下去
logFunc("div", false, true);
};当我们给div的合成事件的事件流中断
就只会执行到合成事件,不会执行原生捕获事件
为什么react17将合成事件挂在根节点
react事件的优先级
通过getEventPriority方法,可以看到每个事件在react中的优先级排序
下面选取部分内容展示优先级排序
case 'cancel':
case 'click':
case 'close':
case 'contextmenu':
case 'copy':
case 'cut':
case 'auxclick':
case 'dblclick':
case 'dragend':
case 'dragstart':
case 'drop':
case 'focusin':
case 'focusout':
case 'input':
case 'invalid':
case 'keydown':
case 'keypress':
case 'keyup':
case 'mousedown':
case 'mouseup':
case 'paste':
case 'pause':
case 'play':
case 'pointercancel':
case 'pointerdown':
case 'pointerup':
case 'ratechange':
case 'reset':
case 'resize':
case 'seeked':
case 'submit':
case 'touchcancel':
case 'touchend':
case 'touchstart':
case 'volumechange':
// Used by polyfills:
// eslint-disable-next-line no-fallthrough
case 'change':
case 'selectionchange':
case 'textInput':
case 'compositionstart':
case 'compositionend':
case 'compositionupdate':
// Only enableCreateEventHandleAPI:
// eslint-disable-next-line no-fallthrough
case 'beforeblur':
case 'afterblur':
// Not used by React but could be by user code:
// eslint-disable-next-line no-fallthrough
case 'beforeinput':
case 'blur':
case 'fullscreenchange':
case 'focus':
case 'hashchange':
case 'popstate':
case 'select':
case 'selectstart':useEffect执行顺序问题
面试被问到,没答出来,记录一下
有这样的一份代码,大概图例如下
代码太长了
import "./styles.css";
import { useEffect } from 'react'
function Grand({ children }) {
useEffect(() => {
console.log('Grand')
}, [])
return (
<div>{children}</div>
)
}
function ParentA({ children }) {
useEffect(() => {
console.log('ParentA')
}, [])
return (
<div>{children}</div>
)
}
function ChildA({ children }) {
useEffect(() => {
console.log('ChildA')
}, [])
return (
<div>{children}</div>
)
}
function GrandsonA({ children }) {
useEffect(() => {
console.log('GrandsonA')
}, [])
return (
<div>{children}</div>
)
}
function ChildB({ children }) {
useEffect(() => {
console.log('ChildB')
}, [])
return (
<div>{children}</div>
)
}
function ParentB({ children }) {
useEffect(() => {
console.log('ParentB')
}, [])
return (
<div>{children}</div>
)
}
function ChildC({ children }) {
useEffect(() => {
console.log('ChildC')
}, [])
return (
<div>{children}</div>
)
}
export default function App() {
return (
<div className="App">
<Grand>
im grand
<ParentA>
im parentA
<ChildA>
im childA
<GrandsonA>
im grandsonA
</GrandsonA>
</ChildA>
<ChildB>
im childB
</ChildB>
</ParentA>
<ParentB>
im parentB
<ChildC>
im childC
</ChildC>
</ParentB>
</Grand>
</div>
);
}