前端面试题整理——React考点和回答
一、基础使用
1、变量、表达式、class style、子元素和组件
2、if else、三元表达式、逻辑运算符 && ||
3、map、key
4、bind this
5、关于event参数,react的event不是原生的,event.nativeEvent才是指向原生event,所有的事件都是挂在在document上。
6、传递自定义参数 ,最后一个参数会自动加上event
7、受控组件,表单的值受到 this.state 的控制影响。
8、组件使用
(1)props 传递数据
(2)props 传递函数
(3)props 类型检查,使用prop-types插件
9、setState(重点)
(1)不可变值
不能直接修改state,使用不可变值
(2)可能是异步更新
setTimeout中的setState是同步的
自定义的 DOM 事件,setState是同步的
(3)可能会被合并
传入函数不会被合并
10、组件生命周期
父子组件生命周期和VUE的调用顺序是一样的。
二、React 高级特性
1、函数组件:纯函数,输入props,输出JSX
没有实例,没有生命周期,没有state,不能扩展其他方法
2、非受控组件
(1)ref
(2)defaultValue、defaultChecked
(3)手动操作DOM元素
非受控组件使用场景:必须手动操作DOM元素,setState实现不了,如文件上传<input type=file>、富文本编辑器
3、Portals
组件默认会按照既定层次嵌套渲染,如何让组件渲染到父组件以外?
使用react-dom中的createPortal,指定渲染dom节点位置
4、context
应用场景:从外层组件给下面所有子层传递简单参数。如设置主题、系统语言。
使用的API,如React.createContext,.Provider,.Consumer
5、异步组件
import()语法,React.lazy,React.Suspense
三、性能优化
1、shouldComponentUpdate(简称SCU)
React 默认父组件有更新,子组件则无条件也更新。
SCU默认返回true,使用SCU对props和states进行前后变化对比来确定组件进不进行渲染。
SCU必须要配合不可变值来进行,不然前后的值都是相等无法进行对比。
2、PureComponent 和 React.memo
PureComponent纯组件,在SCU中实现了浅比较
memo是函数组件中的PureComponent
3、不可变值 immutable.js
彻底拥抱不可变值,基于共享数据不是深拷贝,速度好。
有一定学习和迁移成本,按需使用。
四、组件公共逻辑的抽离
(1)mixin,已被React废弃
(2)高阶组件HOC
高阶组件不是一种功能,而是一种模式,类似于工厂模式。如下代码示例:
const HOCFactory = (Component) =>{ class HOC extends React.Component { // 这里定义多个组件的公共逻辑 render(){ return <Component {...this.props} /> // 返回拼装结果 } } return HOC } const c1 = HOCFactory(Component1) const c2 = HOCFactory(Component2)
如代码所示,传入一个组件,最后返回一个组件。方法中间定义公共逻辑。
Redux 的 connect 就是高阶组件。
(3)Render Props
核心思想是通过一个函数将 class 组件的 state 作为 props 传递给纯函数组件
class Factory extends React.Component { constructor(){ this.state = {} // 这里的 state 是多个组件的公共逻辑的数据 } // 修改state render(){ return <div>{this.props.render(this.state)}</div> } } const App = ()=>( <Factory render={ // render 是一个函数组件 (props) => <p>{props.a}{props.b}</p> }/> )
HOC:模式简单,但会增加组件层级
Render Props:代码简洁,学习成本较高
五、Redux
1、基本概念
store,通过createStore(reducer),传入一个reducer来创建,来存放应用的状态。API有 { subscribe, dispatch, getState }
subscribe 订阅更新
dispatch 改变内部state的唯一方法,dispatch触发一个action
getState 获取state值
state,当state变化时需要返回全新的对象,坚持不可变值原则。
action,修改state值
reducer,形式为(state,action) => state 的纯函数,描述了action如何把state转变成下一个state
2、单项数据流
单项数据流代码步骤:
(1)dispatch(action)
(2)reducer => newState
(3)subscribe 触发通知
3、react-redux
<Provider>
connect
mapStateToProps
mapDispatchToProps
4、异步action
同步是直接返回一个action对象,异步就是返回函数,其中带有dispatch 参数
使用异步action还需要使用中间件创建store,例如使用redux-thunk,以及使用redux的applyMiddleware的API
如:const store = createStore(reducer,applyMiddleware(thunk))
redux-thunk、redux-promise、redux-saga等中间件都可以实现异步action
5、中间件
中间件其实就是改造dispatch,如下自己实现个简单中间件:
// 复制原来的dispatch let next = store.dispatch // 重新定义dispatch store.dispatch = function dispatchAndLog(action){ console.log('执行中间件逻辑',action) // 然后再执行原有dispatch next(action) console.log('执行完后执行的逻辑',store.getState()) }
六、React-router
路由模式(hash、H5 history),同vue-router
路由配置(动态路由、懒加载),同vue-router
七、React 原理
1、函数式编程,重点是不可变值
2、vdom 和 diff算法
3、JSX本质是React.createElement(标签,属性对象,子节点)方法,类似于h函数。
h函数返回的是vnode,那么JSX返回的也是vnode。
第一个参数可能是个组件也可能是个html标签,所以就要求html标签一定是小写,组件名开头要大写,以此来判断。
4、合成事件
所有事件挂载到document上
event不是原生的,是SyntheticEvent合成事件对象
和 vue事件不同,和dom事件也不同
为什么要合成事件机制?
(1)更好的兼容性和跨平台
(2)挂载到domcument,减少内存消耗,避免频繁解绑
(3)方便事件的统一管理(如事务机制)
5、setState batchUpdate
setState:异步(普通使用),同步(setTimeout、DOM事件)
合并(对象形式),不合并(函数形式)
setState主流程:
batchUpdate 机制:
batchUpdate 机制其实是react的方法执行或者生命周期执行,一开始都会先定义一个batchUpdate状态为进行中,
方法结束后将状态改为false表示完成。所以按照eventloop的执行顺序,就会导致setState有时同步有时异步的情况。
React可以管理的入口都会命中batchUpdate机制。
transaction(事务)机制:
transaction 事务机制服务于batchUpdate 机制
6、组件渲染和更新过程
(1)JSX渲染为页面过程
定义props state
解析JSX执行render()函数,生成vnode
patch(elem,vnode),生成页面
(2)setState 之后更新页面过程
setState(newState) => dirtyComponents (可能有子组件)
render() 生成 newVnode
patch(vnode,newVnode)
7、更新的两个阶段
patch拆分成两个阶段:
(1)reconciliation阶段:执行diff算法,纯JS计算
(2)commit阶段:将diff结果渲染DOM
为什么会分成两个阶段?
如果不将patch拆分,可能会有性能问题。
因为JS是单线程,且和DOM渲染共用一个线程,当组件足够复杂,组件更新时计算和渲染压力都很大,
同时再有DOM操作需求(动画、拖拽等),可能会出现卡顿。
解决上面的性能问题进行拆分的具体解决方案是 fiber:
将 reconciliation 阶段进行任务拆分(commit无法拆分),
DOM需要渲染时暂停任务,空闲时恢复执行拆分的任务。
通过window.requestIdleCallback这个API来捕获DOM渲染时机,从而调配拆分的任务。但是该API有浏览器兼容限制。
面试真题:
1、React组件如何通讯
父子组件props
自定义事件
Redux 和 Context
2、JSX本质是什么
createElement函数
执行返回 vnode
3、context是什么,有何用途
父组件向下所有子孙组件传递信息,一些简单公共信息如主题色、语言等。复杂的公共信息用Redux
4、shouldComponentUpdate(SCU)的用途
性能优化,配合不可变值一起使用,否则会出错
5、描述redux单向数据流
(1)dispatch(action)
(2)reducer => newState
(3)subscribe 触发通知
6、setState是同步还是异步
setState主流程,batchUpdate机制。
7、什么时纯函数?
返回一个新值,没有副作用(不会修改其他值),重点就是不可变值。
8、函数组件和 class 组件的区别
函数组件:纯函数,输入props,输出JSX
没有实例,没有生命周期,没有state,不能扩展其他方法
9、什么是受控组件?
表单的值,受state控制,需要自行监听onChange,更新state
10、React 性能优化
渲染列表时加key
自定义事件、DOM事件及时销毁
合理使用异步组件
减少函数bind this的次数
合理使用 SCU、PureComponent 和 memo
合理使用 Immutable.js
webpack层面的优化,前端通用的性能优化
11、React 和 Vue 的区别
共同点:
都支持组件化
都是数据驱动视图
都是使用 vdom 操作 DOM
不同点:
React 使用 JSX 拥抱 JS,Vue 使用模板拥抱 html
React 函数式编程,Vue 声明式编程
React 更多需要自力更生,Vue 把你想要的都给你