React setState有同步有异步,那么同情况下的Vue呢?
首先:关于 React useState和setState到底是同步还是异步?
可以看这位朋友写的:https://juejin.cn/post/6959885030063603743#heading-6
总结一下(React useState和setState到底是同步还是异步?)
- 通过react事件流控制的(比如onClick)或生命周期或hook(总之是react api控制的),就是异步的
- setState和useState都是异步执行的(不会立即更新state的结果)
- 多次执行setState和useState,只会调用一次重新渲染render
- 不同的是,setState会进行state的合并,而useState则不会
- 通过js原生api,比如setTimeout,promise,addEventListener等。都超出了react的控制范围的,就是同步执行的(有几个setState就render几次)
- setState和useState是同步执行的(立即更新state的结果)
- 多次执行setState和useState,每一次的执行setState和useState,都会调用一次render
此例子能说明(注意看注释):
react在原生js api内执行多个setState 有性能问题
- 放在 promise和addEventListener内的 setState 是同步的,会render N次(性能更不好,重复render了)
class Component extends React.Component {
constructor(props) {
super(props)
this.state = {
a: 1,
}
}
componentDidMount() {
document.addEventListener('click', () => { // 非按钮处,点击一下
this.handleClickWithoutPromise() // 两次 setState 各自 render 一次,分别打印 2,3
})
}
handleClickWithPromise = () => {
Promise.resolve().then(() => {
this.setState({a: this.state.a + 1})
this.setState({a: this.state.a + 1})
console.log(this.state.a) // 同步执行 拿最终结果 3
})
}
handleClickWithoutPromise = () => {
this.setState({a: this.state.a + 1})
this.setState({a: this.state.a + 1})
console.log(this.state.a) // 异步执行 拿初始值 1
}
render() {
// 当点击同步执行按钮时,两次 setState 合并,只执行了最后一次,此处log 打印 2
// 当点击异步执行按钮时,两次 setState 各自 render 一次,此处log分别 打印 2,3
console.log('a', this.state.a)
return (
<>
{this.state.a}
<button onClick={this.handleClickWithPromise}>放在promise内, 结果是同步执行setState, 会render N次, 损害性能</button>
<button onClick={this.handleClickWithoutPromise}>受react控制, 异步执行, 会合并setState, 只会render 1次, 性能更好</button>
</>
)
}
}
相同的demo,在vue中是什么情况呢?
先说结论:
对于vue来说,并没有这种性能问题,vue不受js原生api的影响
- 因为vue是对数据的变化做劫持,无论是否是原生js api操作数据,数据都可以被劫持到
- 数据劫持的api的使用(源码自带):vue2是es5的Object.definedProperty。vue3是es6的new Proxy()
- vue会把一次同步任务内的操作,都合并起来,只render一次。 不受setTimeout,promise,addEventListener等api的影响
以下demo配置:vue3 + jsx 写法 (vue3和vue2都是对数据做劫持)
- 注意看注释,和按钮的名字
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
const a = ref(1) // 不熟悉vue的,可以把这个理解为 react的 const [a, setA] = useState(1)
console.log(a.value) // 打印a的值
return () => {
console.log(a.value) // 此处可以测试,vue的render的次数
return (
<div class="projectList">
<div>{a.value}</div>
<button onClick={() => {
a.value = a.value + 1
a.value = a.value + 1
console.log(a.value) // 正常打印3,只触发一次render
}}>普通按钮,正常打印,只触发一次render</button>
<button onClick={() => {
setTimeout(() => {
a.value = a.value + 1
a.value = a.value + 1
console.log(a.value) // 正常打印3,只触发一次render
})
}}>不受setTimeout影响,正常打印,只触发一次render</button>
<button onClick={() => {
setTimeout(() => { // 第一个异步任务
a.value = a.value + 1
a.value = a.value + 1
console.log(a.value) // 正常打印3,触发第一次render
setTimeout(() => { // 第二个异步任务
a.value = a.value + 1
a.value = a.value + 1
console.log(a.value) // 正常打印5,触发第二次render
})
})
}}>两层setTimeout按钮,只受异步任务的影响,正常打印,只触发两次render</button>
</div>
)
}
}
})
总结
react的多个setState并发执行 区分同步和异步
- 正常如果受react api(react事件流、生命周期钩子、hook)控制的话,就是异步的(提高性能,只会render一次)
- 如果不受react控制(比如js原生api:setTimeout,promise,addEventListener等),就会同步执行,render N次,性能更不好
对于vue来说,并没有这种性能问题,vue不受js原生api的影响
- 因为vue是对数据的变化做劫持,无论是否是原生js api操作数据,数据都可以被劫持到
- vue会把一次同步任务内的操作,都合并起来,只render一次。 不受setTimeout,promise,addEventListener等api的影响
多个数据并发被修改的情况,vue是同步还是异步? - 数据的变更是同步的。但是render是异步的
- 可以理解为:一次同步任务内,无论怎么同步执行修改数据,都不会render,
**最终的render会在同步任务结束后的 异步微任务中**
去执行- vue的源码内,就是用Promise.resolve().then( 在这里面执行render )
- 微任务的优先级比setTimeout要高,所以两层setTimeout会执行2次render
谢谢思考!
码字不易,点赞鼓励!