共计 3713 个字符,预计需要花费 10 分钟才能阅读完成。
setState()
说明
更新数据
setState()
是异步更新数据的- 可以多次调用
setState()
,之后触发一次重新渲染(render) - 使用下面这种语法时,后面的
setState()
不要依赖于前面的setState()
this.state = {
count: 1
}
this.setState({
count: this.state.count + 1
})
console.log(this.state.count) // 1
this.setState({
count: this.state.count + 1 // 1 + 1
})
console.log(this.state.count) // 1
推荐语法 setState((state, props) => { })
- 使用
setState((state, props) => { })
语法 - 参数
state
表示最新的satte
- 参数
props
表示最新的props
// 这种语法也是异步更新 state 的
this.setState((state, props) => {
return {
count: state.count + 1
}
})
console.log(this.state.count) // 1
this.setState((state, props) => {
console.log('第二次调用', state.count) // 2
return {
count: state.count + 1
}
})
console.log(this.state.count) // 1
第二个参数
- 场景:在状态更新(页面完成渲染)后立即执行某个操作
- 语法:
setState(updater[, callback])
this.setState((state, props) => {
return {
count: state.count + 1
}
}, () => {
// 状态更新后并且重新渲染后,立即执行
console.log('状态更新完成', this.state.count) // 再打印 2
document.title = '更新 state 后的标题' + this.state.count
})
console.log(this.state.count) // 先打印 1
setState
处在同步的逻辑中,异步更新状态,异步更新真实 dom
setState
处在异步的逻辑中,同步更新状态,同步更新真实 dom
JSX 语法的转化过程
- JSX 仅仅是
createElement()
方法的语法糖(简化语法) - JSX 语法被
@bable/preset-react
插件编译为createElement()
方法 - React 元素是一个对象,用来描述你希望再屏幕上看到的内容
组件更新机制
setState()
两个作用:1.修改 state、2.更新组件(UI)- 过程:父组件重新渲染时,也会重新渲染子组件;但只会渲染当前组件子树(当前组件及其所有子组件)
组件性能优化
减轻 state
- state 是组件的状态,只存储根组件渲染相关的数据(比如:count / 列表数据 / loading 等)
- 不用做渲染的数据不要放在 state 中,比如定时器id等
- 对于这种需要在多个方法中用到的数据,应该放在 this 中
class Hello extends Component {
componentDidMount() {
// timerId 存储到 this 中,而不是 state 中
this.timerId = setInterval( () => { }, 2000 )
}
componentWillUnmount() {
clearInterval(this.timerId)
}
render() { ... }
}
避免不必要的重新渲染
- 父组件更新会引起子组件也被更新,这种思路很清晰
- 问题:子组件没有任何变化时也会重新渲染
- 解决方式:使用钩子函数
shouldComponentUpdate(nextProps, nextState)
- 作用:通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 反之
- 触发时机:更新阶段的钩子函数,组件重新渲染前执行(
shouldComponentUpdate -> render
)
class Hello extends Component {
shouldComponentUpdate(nextProps, nextState) {
// 根据条件,决定是否重新渲染组件
console.log(this.state) // 更新前的状态
console.log(nextProps) // 最新的状态
return false
}
render() { ... }
}
纯组件
React.PureComponent
与React.Component
功能相似- 区别:
PureComponent
内部自动实现了shouldComponentUpdate
钩子,不需要手动比较 - 原理:纯组件内部通过分别对比前后两次 props 和 state 的值,来决定是否重新渲染组件
class Hello extends React.PureComponent {
render() {
return (
<div>纯组件</dic>
)
}
}
- 纯组件内部的对比是
shallow compare
(浅层对比) - 对于值类型来说:比较两个值是否相同(直接赋值即可)
let number = 0
let newNumber = number
newNumber = 2
console.log(number === newNumber) // false
state = {
number: 0
}
this.setState({
number: Math.floor(Math.random() * 3)
})
// PureComponeent 内部对比:
最新的 state.number === 上一次的 satte.number // false 重新渲染组件
- 对于引用类型来说:只比较对象的引用(地址)是否相同
- state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据
const obj = {
number: 0
}
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true
state = {
obj: {
number: 0
}
}
// 错误做法
this.state.obj.number = 2
this.setState({
obj: state.obj
})
// PureComponent 内部比较
最新的 state.obj === 上一次的 satte.obj // true 不重新渲染组件
// 正确 创建新数据
const newObj = { ...state.obj, number: 2 }
this.setState({ obj: newObj })
// 正确 创建新数据
// 不要用数组的 push / unshift 等直接修改当前数组的方法
// 应该用 concat 或 slice 等这些返回新数组的方法
this.setState({
list: [...this.state.list, { 新数据 }]
})
虚拟 DOM 和 Diffing 算法
- React 更新视图的思想是:只要 state 变化就重新渲染视图
问题:组件中只有一个 DOM 元素需要更新时,也得把整个组件的内容重新渲染到页面中?不是
理想状态:部分更新,只更新变化的地方
问题:React 是如何做到部分更新的?虚拟 DOM 配合 Diff 算法
- 虚拟 DOM:本质上就是 JS 对象,用来描述你希望在屏幕上看到的内容(UI)
执行过程
- 初次渲染时,React 会根据初始 state(Model),创建一个虚拟 DOM 对象(树)
- 根据虚拟 DOM 生成真正的 DOM,渲染到页面中
- 当数据变化后(setState()),重新根据新的数据,重新新的虚拟 DOM 对象(树)
- 与上一次得到的虚拟 DOM 对象,使用 Diff 算法对比(找不同),得到需要更新的内容
- 最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面
react/vue中的key有什么作用?(key的内部原理是什么?)
为什么遍历列表时,key最好不要用index?
一. 虚拟DOM中key的作用:
- 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
- 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
- a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变, 直接使用之前的真实DOM
- 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- b. 旧虚拟DOM中未找到与新虚拟DOM相同的key根据数据创建新的真实DOM,随后渲染到到页面
- a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
二. 用index作为key可能会引发的问题:
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
- 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
- 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
三. 开发中如何选择key?:
- 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 如果确定只是简单的展示数据,用index也是可以的
正文完