性能优化
性能优化,永远是面试的重点,性能优化对于 React 更加重要
- 在页面中使用了
setTimout()
、addEventListener()
等,要及时在componentWillUnmount()
中销毁 - 使用异步组件
- 使用
React-loadable
动态加载组件 shouldComponentUpdate
(简称SCU )、React.PureComponent
、React.memo
- 不可变值 ImmutableJS
shouldComponentUpdate (nextProps, nextState) {
return true // 可以渲染,执行 render(),默认返回 true
return false // 不能渲染,不执行 render()
}
什么情况下需要使用 shouldComponentUpdate
在React中,默认情况下,如果父组件数据发生了更新,那么所有子组件都会无条件更新 !!!
通过shouldComponentUpdate()
retrun fasle 来判断阻止 Header 组件做无意义的更新
shouldComponentUpdate()
并不是每次都需要使用,而是需要的时候才会优化
class App extends React.Component {
constructor () {
this.state = {
list: [] }
}
render () {
return (
<div>
{
/* 当list数据发生变化时,Header组件也会更新,调用 render() */}
<Header />
<List data={
this.state.list}
</div>
)
}
}
在shouldComponentUpdate()
判断中,有一个有意思的问题,解释为什么 React setState()
要用不可变值
// 父组件中
changeList () {
this.state.list.push({
id: 2})
this.setState({
list: this.state.list
})
}
// 子组件中
import _ from 'lodash'
shouldComponentUpdate(nextProps, nextState) {
// 数组深度比较(一次性递归到底,耗费性能,工作中慎用)
if (_.isEqual(nextProps.list, this.props.list)) {
return false // 相等,不渲染
}
return true // 不相等,渲染
}
子组件将始终不会渲染,因为在
shouldComponentUpdate()
中,this.state.list.push()
已经修改了this.props.list
,而this.setState()
修改了nextProps.list
所以两个值深度比较,将始终相同。
PureComponent 和 memo
- class类组件中用
PureComponent
,无状态组件(无状态)中用memo
- PureComponent, SCU中实现了浅比较
- 浅比较已使用大部分情况(尽量不要做深度比较)
PureComponent 与普通 Component 不同的地方在于,PureComponent自带了一个
shouldComponentUpdate()
,并且进行了浅比较
// memo用法
function MyComponent (props) {
/* 使用 props 渲染 */
}
// areEqual 也可不传
function areEqual(prevProps, nextProps) {
if (prevProps.seconds===nextProps.seconds) {
return true
} else {
return false
}
}
export default React.memo(MyComponent, areEqual)
immutable.js
- 彻底拥抱“不可变值”
- 基础共享数据(不是深拷贝),速度快
- 有一定学习和迁移成本
参考 前端进阶面试题详细解答
常见基础面试考题
React 组件如何通讯
- 父子组件通过 属性 和 props 通讯
- 通过 context 通讯
- 通过 Redux 通讯
this.setState()相关
import React from 'react'
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
count: 0 }
}
componentDidMount () {
this.setState({
count: this.state.count + 1 })
console.log(this.state.count) // 0
this.setState({
count: this.state.count + 1 })
console.log(this.state.count) // 0
setTimeout(() => {
this.setState({
count: this.state.count + 1 })
console.log(this.state.count) // 2
}, 0)
setTimeout(() => {
this.setState({
count: this.state.count + 1 })
console.log(this.state.count) // 3
}, 0)
// setTimeout(function () {
// this.setState({count: this.state.count + 1 })
// console.log(this.state.count) // 报错,this 指向问题
// }, 0)
}
render () {
return <h1>{
this.state.count}</h1>
}
}
export default App // 返回高阶函数
JSX本质是什么…
前端富文本 dangerouslySetInnerHTML
const rawHtml = '<div><p>Title</p></div>'
const rawHtmlData = {
__html: rawHtml // 这里有个下划线
}
return <div dangerouslySetInnerHTML={
rawHtmlData}></div>
两种绑定事件
<button onClcik={
bindClcik1.bind(this)}> 使用 .bind(this) </button>
<button onClcik={
bindClcik2}> 箭头函数 </button>
// 使用 class 的自带函数,需要重定向 this
bindClcik1 () {
alert('bindClcik1') }
// 使用静态方法,使用箭头函数不需要使用 bind(this)
bindClick2 = () => {
alert('bindClcik2') }
Event、默认事件、事件冒泡
这里打印出来的Event
对象是 React 封装过的SyntheticEvent
,可以看__proto__.constructor
。React 标准化了事件对象,因此在不同的浏览器中都会有相同的属性。
React 中事件绑定跟 Vue 中完全不同,Vue中事件绑定和触发的对象为同一元素,React中事件触发的对象为document
,绑定元素为当前元素。React的所有事件都会被挂载到document
上和DOM
事件不同。
Vue 的Event
是原生,事件被挂载到当前元素和DOM
事件一
<a href="www.baidu.com" onClick={
this.getEvent} target="blank">Get Event</a>
getEvent = (event) => {
event.preventDefault() // 阻止默认事件
event.stopPropagation() // 阻止事件冒泡
console.log(event) // 非原生的 Event
console.log(event.nativeEvent) // 获取原生的 Event
console.log(event.nativeEvent.target) // 绑定事件的对象,这里为 <a></a>
console.log(event.nativeEvent.currentTarget) // 触发事件的对象,这里为 document
}
事件传参
通过.bind()
传参
<div onClick={this.getParams1.bind(this, 'id1', 'title1')}>get params 1</div>
getParams1 (id, title, event) {
console.log('id', id)
console.log('title', title)
console.log('event', event) // 最后一个参数为Event对象
}
通过箭头函数传参
<div onClick={
(event) => {
this.getParams2('id2', 'title2', event) }}>get params 2</div>
getParams2 (id, title, event) {
console.log('id', id)
console.log('title', title)
console.log('event', event)
}
表单
<div>
<label htmlFor="userName"></label>
<input value={
this.state.userName} onChange={
this.handleInputChange.bind(this)} /