2021⾼频前端⾯试题汇总之React篇
2021⾼频前端⾯试题汇总之React篇
React视频教程系列
React 实战:CNode视频教程
完整教程⽬录:
1. React 事件机制
<div onClick={this.handleClick.bind(this)}>点我</div>
复制代码
React并不是将click事件绑定到了div的真实DOM上,⽽是在document处监听了所有的事件,当事件发⽣并且冒泡到document处的时
候,React将事件内容封装并交由真正的处理函数运⾏。这样的⽅式不仅仅减少了内存的消耗,还能在组件挂在销毁时统⼀订阅和移除事件。
除此之外,冒泡到document上的事件也不是原⽣的浏览器事件,⽽是由react⾃⼰实现的合成事件(SyntheticEvent)。因此如果不想要是事件冒泡的话应该调⽤event.preventDefault()⽅法,⽽不是调⽤event.stopProppagation()⽅法。
JSX 上写的事件并没有绑定在对应的真实 DOM 上,⽽是通过事件代理的⽅式,将所有的事件都统⼀绑定在了document上。这样的⽅式不仅减少了内存消耗,还能在组件挂载销毁时统⼀订阅和移除事件。
另外冒泡到document上的事件也不是原⽣浏览器事件,⽽是 React ⾃⼰实现的合成事件(SyntheticEvent)。因此我们如果不想要事件冒泡的话,调⽤event.stopPropagation是⽆效的,⽽应该调⽤event.preventDefault。
530是什么意思
实现合成事件的⽬的如下:
合成事件⾸先抹平了浏览器之间的兼容问题,另外这是⼀个跨浏览器原⽣事件包装器,赋予了跨浏览器开发的能⼒;
对于原⽣浏览器事件来说,浏览器会给监听器创建⼀个事件对象。如果你有很多的事件监听,那么就需要分配很多的事件对象,造成⾼额的内存分配问题。但是对于合成事件来说,有⼀个事件池专门来
管理它们的创建和销毁,当事件需要被使⽤时,就会从池⼦中复⽤对象,事件回调结束后,就会销毁事件对象上的属性,从⽽便于下次复⽤事件对象。
2. React ⾼阶组件、Render props、hooks 有什么区别,为什么要不断迭代
这三者是⽬前react解决代码复⽤的主要⽅式:
⾼阶组件(HOC)是 React 中⽤于复⽤组件逻辑的⼀种⾼级技巧。HOC ⾃⾝不是 React API 的⼀部分,它是⼀种基于 React 的组合特性⽽形成的设计模式。具体⽽⾔,⾼阶组件是参数为组件,返回值为新组件的函数。
render props是指⼀种在 React 组件之间使⽤⼀个值为函数的 prop 共享代码的简单技术,更具体的说,render prop 是⼀个⽤于告知组件需要渲染什么内容的函数 prop。
通常,render props 和⾼阶组件只渲染⼀个⼦节点。让 Hook 来服务这个使⽤场景更加简单。这两种模式仍有⽤武之地,(例如,⼀个虚拟滚动条组件或许会有⼀个 renderltem 属性,或是⼀个可见的容器组件或许会有它⾃⼰的 DOM 结构)。但在⼤部分场景下,Hook ⾜够了,并且能够帮助减少嵌套。
(1)HOC 官⽅解释∶
⾼阶组件(HOC)是 React 中⽤于复⽤组件逻辑的⼀种⾼级技巧。HOC ⾃⾝不是 React API 的⼀部分,它是⼀种基于 React 的组合特性⽽形成的设计模式。
wight简⾔之,HOC是⼀种组件的设计模式,HOC接受⼀个组件和额外的参数(如果需要),返回⼀个新的组件。HOC 是纯函数,没有副作⽤。
// hoc的定义
function withSubscription(WrappedComponent, lectData) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: lectData(DataSource, props)
};
}
// ⼀些通⽤的逻辑处理
render() {
// ... 并使⽤新数据渲染被包装的组件!
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
// 使⽤
const BlogPostWithSubscription = withSubscription(BlogPost,
(DataSource, props) => BlogPost(props.id));
复制代码
康宝蓝
HOC的优缺点∶
优点∶逻辑服⽤、不影响被包裹组件的内部逻辑。
缺点∶ hoc传递给被包裹组件的props容易和被包裹后的组件重名,进⽽被覆盖
(2)Render props 官⽅解释∶
"render prop"是指⼀种在 React 组件之间使⽤⼀个值为函数的 prop 共享代码的简单技术
具有render prop 的组件接受⼀个返回React元素的函数,将render的渲染逻辑注⼊到组件内部。在这⾥,"render"的命名可以是任何其他有效的标识符。
// DataProvider组件内部的渲染逻辑如下
class DataProvider extends React.Components {
state = {
name: 'Tom'
}
render() {
return (
<div>
<p>共享数据组件⾃⼰内部的渲染逻辑</p>
{ der(this.state) }
</div>
);
}
}
// 调⽤⽅式
<DataProvider render={data => (
<h1>Hello {data.name}</h1>
)}/>
复制代码
由此可以看到,render props的优缺点也很明显∶
优点:数据共享、代码复⽤,将组件内的state作为props传递给调⽤者,将渲染逻辑交给调⽤者。
缺点:⽆法在 return 语句外访问数据、嵌套写法不够优雅
(3)Hooks 官⽅解释∶
Hook是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使⽤ state 以及其他的 React 特性。通过⾃定义hook,可以复⽤代码逻辑。
// ⾃定义⼀个获取订阅数据的hook
function uSubscription() {
const data = Comments();
return [data];
}
//
function CommentList(props) {
const {data} = props;
const [subData] = uSubscription();
...
}
// 使⽤
<CommentList data='hello' />
复制代码
以上可以看出,hook解决了hoc的prop覆盖的问题,同时使⽤的⽅式解决了render props的嵌套地狱的问题。hook的优点如下∶
使⽤直观;
解决hoc的prop 重名问题;
解决render props 因共享数据⽽出现嵌套地狱的问题;
能在return之外使⽤数据的问题。
需要注意的是:hook只能在组件顶层使⽤,不可在分⽀语句中使⽤。
总结∶ Hoc、render props和hook都是为了解决代码复⽤的问题,但是hoc和render props都有特定的使⽤场景和明显的缺点。hook是
react16.8更新的新的API,让组件逻辑复⽤更简洁明了,同时也解决了hoc和render props的⼀些缺点。
3. React.Component 和 React.PureComponent 的区别
PureComponent表⽰⼀个纯组件,可以⽤来优化React程序,减少render函数执⾏的次数,从⽽提⾼组件的性能。
在React中,当prop或者state发⽣变化时,可以通过在shouldComponentUpdate⽣命周期函数中执⾏return fal来阻⽌页⾯的更新,从⽽减
少不必要的render执⾏。React.PureComponent会⾃动执⾏ shouldComponentUpdate。
不过,pureComponent中的 shouldComponentUpdate() 进⾏的是浅⽐较,也就是说如果是引⽤数据类型的数据,只会⽐较不是同⼀个地址,⽽不会⽐较这个地址⾥⾯的数据是否⼀致。浅⽐较会忽略属性和或状态突变情况,其实也就是数据引⽤指针没有变化,⽽数据发⽣改变的时候render是不会执⾏的。如果需要重新渲染那么就需要重新开辟空间引⽤数据。PureComponent⼀般会⽤在⼀些纯展⽰组件上。
使⽤pureComponent的好处:当组件更新时,如果组件的props或者state都没有改变,render函数就不会触发。省去虚拟DOM的⽣成和对⽐过程,达到提升性能的⽬的。这是因为react⾃动做了⼀层浅⽐较。
4. Redux 中异步的请求怎么处理
可以在 componentDidmount 中直接进⾏请求⽆须借助redux。但是在⼀定规模的项⽬中,上述⽅法很难进⾏异步流的管理,通常情况下我们会借助redux的异步中间件进⾏异步处理。redux异步流中间件其实有很多,当下主流的异步中间件有两种redux-thunk、redux-saga。
(1)使⽤react-thunk中间件
winter是什么意思redux-thunk优点:
体积⼩: redux-thunk的实现⽅式很简单,只有不到20⾏代码
使⽤简单: redux-thunk没有引⼊像redux-saga或者redux-obrvable额外的范式,上⼿简单
redux-thunk缺陷:
样板代码过多: 与redux本⾝⼀样,通常⼀个请求需要⼤量的代码,⽽且很多都是重复性质的
耦合严重: 异步操作与redux的action偶合在⼀起,不⽅便管理
功能孱弱: 有⼀些实际开发中常⽤的功能需要⾃⼰进⾏封装
使⽤步骤:
配置中间件,在store的创建中配置
艾滋病英文名简称import {createStore, applyMiddleware, compo} from 'redux';
import reducer from './reducer';
import thunk from 'redux-thunk'
// 设置调试⼯具
const compoEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compo; // 设置中间件
const enhancer = compoEnhancers(
applyMiddleware(thunk)
);
const store = createStore(reducer, enhancer);
export default store;
复制代码
添加⼀个返回函数的actionCreator,将异步请求逻辑放在⾥⾯
/**
几点了用英语怎么说
发送get请求,并⽣成相应action,更新store的函数
@param url {string} 请求地址
@param func {function} 真正需要⽣成的action对应的actionCreator
@return {function}
*/
// dispatch为⾃动接收的store.dispatch函数
export const getHttpAction = (url, func) => (dispatch) => {
<(url).then(function(res){
const action = func(res.data)
dispatch(action)
})
}
复制代码
⽣成action,并发送action
componentDidMount(){
var action = getHttpAction('/getData', getInitTodoItemAction)
// 发送函数类型的action时,该action的函数体会⾃动执⾏
store.dispatch(action)
}
复制代码
(2)使⽤redux-saga中间件
redux-saga优点:
异步解耦: 异步操作被被转移到单独 saga.js 中,不再是掺杂在 action.js 或 component.js 中
action摆脱thunk function: dispatch 的参数依然是⼀个纯粹的 action (FSA),⽽不是充满 “⿊魔法” thunk function
异常处理: 受益于 generator function 的 saga 实现,代码异常/请求失败都可以直接通过 try/catch 语法直接捕获处理
功能强⼤: redux-saga提供了⼤量的Saga 辅助函数和Effect 创建器供开发者使⽤,开发者⽆须封装或者简单封装即可使⽤
灵活: redux-saga可以将多个Saga可以串⾏/并⾏组合起来,形成⼀个⾮常实⽤的异步flow
易测试,提供了各种ca的测试⽅案,包括mock task,分⽀覆盖等等
redux-saga缺陷:
额外的学习成本: redux-saga不仅在使⽤难以理解的 generator function,⽽且有数⼗个API,学习成本远超redux-thunk,最重要的是你的额外学习成本是只服务于这个库的,与redux-obrvable不同,redux-obrvable虽然也有额外学习成本但是背后是rxjs和⼀整套思想体积庞⼤: 体积略⼤,代码近2000⾏,min版25KB左右
功能过剩: 实际上并发控制等功能很难⽤到,但是我们依然需要引⼊这些代码
ts⽀持不友好: yield⽆法返回TS类型
redux-saga可以捕获action,然后执⾏⼀个函数,那么可以把异步代码放在这个函数中,使⽤步骤如下:
配置中间件
import {createStore, applyMiddleware, compo} from 'redux';
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga'
import TodoListSaga from './sagas'
const compoEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compo; const sagaMiddleware = createSagaMiddleware()
const enhancer = compoEnhancers(
applyMiddleware(sagaMiddleware)
);
const store = createStore(reducer, enhancer);
sagaMiddleware.run(TodoListSaga)
export default store;
复制代码
将异步请求放在sagas.js中
import {takeEvery, put} from 'redux-saga/effects'
import {initTodoList} from './actionCreator'
import {GET_INIT_ITEM} from './actionTypes'
import axios from 'axios'
function* func(){
try{
// 可以获取异步返回数据
const res = ('/getData')
const action = initTodoList(res.data)
// 将action发送到reducer
yield put(action)
}catch(e){
console.log('⽹络请求失败')
}
}
function* mySaga(){
// ⾃动捕获GET_INIT_ITEM类型的action,并执⾏func
yield takeEvery(GET_INIT_ITEM, func)
}
export default mySaga
复制代码
发送action
componentDidMount(){
const action = getInitTodoItemAction()
store.dispatch(action)
}
5. Redux 中间件是什么?接受⼏个参数?柯⾥化函数两端的参数具体是什么?
Redux 的中间件提供的是位于 action 被发起之后,到达 reducer 之前的扩展点,换⽽⾔之,原本 view -→> action -> reducer -> store 的数据流加上中间件后变成了 view -> action -> middleware -> reducer -> store ,在这⼀环节可以做⼀些"副作⽤"的操作,如异步请求、打印⽇志等。
applyMiddleware源码:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
// 利⽤传⼊的createStore和reducer和创建⼀个store
const store = createStore(...args)守护甜心66
let dispatch = () => {
throw new Error()
}
const middlewareAPI = {
getState: State,
dispatch: (...args) => dispatch(...args)
}
// 让每个 middleware 带着 middlewareAPI 这个参数分别执⾏⼀遍
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 接着 compo 将 chain 中的所有匿名函数,组装成⼀个新的函数,即新的 dispatch
dispatch = compo(...chain)(store.dispatch)
return {
...store,
dispatch
}
ntr是什么的简称
}
}
cppcc复制代码
从applyMiddleware中可以看出∶
redux中间件接受⼀个对象作为参数,对象的参数上有两个字段 dispatch 和 getState,分别代表着 Redux Store 上的两个同名函数。
柯⾥化函数两端⼀个是 middewares,⼀个是store.dispatch
6. 对 React Hook 的理解,它的实现原理是什么
React-Hooks 是 React 团队在 React 组件开发实践中,逐渐认知到的⼀个改进点,这背后其实涉及对类组件和函数组件两种组件形式的思考和侧重。
(1)类组件:所谓类组件,就是基于 ES6 Class 这种写法,通过继承 React.Component 得来的 React 组件。以下是⼀个类组件:
class DemoClass extends React.Component {
state = {
text: ""
};
componentDidMount() {
//...
}
changeText = (newText) => {
this.tState({
text: newText
});
};
render() {
return (
<div className="demoClass">
<p>{}</p>
<button onClick={this.changeText}>修改</button>
</div>
)
;
}
}
复制代码
可以看出,React 类组件内部预置了相当多的“现成的东西”等着我们去调度/定制,state 和⽣命周期就是这些“现成东西”中的典型。要想得到这些东西,难度也不⼤,只需要继承⼀个 React.Component 即可。
当然,这也是类组件的⼀个不便,它太繁杂了,对于解决许多问题来说,编写⼀个类组件实在是⼀个过于复杂的姿势。复杂的姿势必然带来⾼昂的理解成本,这也是我们所不想看到的。除此之外,由于开发者编写的逻辑在封装后是和组件粘在⼀起的,这就使得类组件内部的逻辑难以实现拆分和复⽤。
(2)函数组件:函数组件就是以函数的形态存在的 React 组件。早期并没有 React-Hooks,函数组件内部⽆法定义和维护 state,因此它还有⼀个别名叫“⽆状态组件”。以下是⼀个函数组件:
function DemoFunction(props) {
const { text } = props
return (
<div className="demoFunction">
杭州会计培训<p>{`函数组件接收的内容:[${text}]`}</p>
</div>
);
}
复制代码
相⽐于类组件,函数组件⾁眼可见的特质⾃然包括轻量、灵活、易于组织和维护、较低的学习成本等。