React setState有同步有异步,那么同情况下的Vue呢?

首先:关于 React useState和setState到底是同步还是异步?

可以看这位朋友写的:https://juejin.cn/post/6959885030063603743#heading-6

总结一下(React useState和setState到底是同步还是异步?)

  1. 通过react事件流控制的(比如onClick)或生命周期或hook(总之是react api控制的),就是异步
  • setState和useState都是异步执行的(不会立即更新state的结果)
  • 多次执行setState和useState,只会调用一次重新渲染render
  • 不同的是,setState会进行state的合并,而useState则不会
  1. 通过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了)
  1. class Component extends React.Component {
  2. constructor(props) {
  3. super(props)
  4. this.state = {
  5. a: 1,
  6. }
  7. }
  8. componentDidMount() {
  9. document.addEventListener('click', () => { // 非按钮处,点击一下
  10. this.handleClickWithoutPromise() // 两次 setState 各自 render 一次,分别打印 2,3
  11. })
  12. }
  13. handleClickWithPromise = () => {
  14. Promise.resolve().then(() => {
  15. this.setState({a: this.state.a + 1})
  16. this.setState({a: this.state.a + 1})
  17. console.log(this.state.a) // 同步执行 拿最终结果 3
  18. })
  19. }
  20. handleClickWithoutPromise = () => {
  21. this.setState({a: this.state.a + 1})
  22. this.setState({a: this.state.a + 1})
  23. console.log(this.state.a) // 异步执行 拿初始值 1
  24. }
  25. render() {
  26. // 当点击同步执行按钮时,两次 setState 合并,只执行了最后一次,此处log 打印 2
  27. // 当点击异步执行按钮时,两次 setState 各自 render 一次,此处log分别 打印 2,3
  28. console.log('a', this.state.a)
  29. return (
  30. <>
  31. {this.state.a}
  32. <button onClick={this.handleClickWithPromise}>放在promise内, 结果是同步执行setState, render N次, 损害性能</button>
  33. <button onClick={this.handleClickWithoutPromise}>受react控制, 异步执行, 会合并setState, 只会render 1次, 性能更好</button>
  34. </>
  35. )
  36. }
  37. }

相同的demo,在vue中是什么情况呢?

先说结论:

对于vue来说,并没有这种性能问题,vue不受js原生api的影响

  1. 因为vue是对数据的变化做劫持无论是否是原生js api操作数据,数据都可以被劫持到
    • 数据劫持的api的使用(源码自带):vue2是es5的Object.definedProperty。vue3是es6的new Proxy()
  2. vue会把一次同步任务内的操作,都合并起来,只render一次。 不受setTimeout,promise,addEventListener等api的影响

以下demo配置:vue3 + jsx 写法 (vue3和vue2都是对数据做劫持)

  • 注意看注释,和按钮的名字
  1. import { defineComponent, ref } from 'vue'
  2. export default defineComponent({
  3. setup () {
  4. const a = ref(1) // 不熟悉vue的,可以把这个理解为 react的 const [a, setA] = useState(1)
  5. console.log(a.value) // 打印a的值
  6. return () => {
  7. console.log(a.value) // 此处可以测试,vue的render的次数
  8. return (
  9. <div class="projectList">
  10. <div>{a.value}</div>
  11. <button onClick={() => {
  12. a.value = a.value + 1
  13. a.value = a.value + 1
  14. console.log(a.value) // 正常打印3,只触发一次render
  15. }}>普通按钮,正常打印,只触发一次render</button>
  16. <button onClick={() => {
  17. setTimeout(() => {
  18. a.value = a.value + 1
  19. a.value = a.value + 1
  20. console.log(a.value) // 正常打印3,只触发一次render
  21. })
  22. }}>不受setTimeout影响,正常打印,只触发一次render</button>
  23. <button onClick={() => {
  24. setTimeout(() => { // 第一个异步任务
  25. a.value = a.value + 1
  26. a.value = a.value + 1
  27. console.log(a.value) // 正常打印3,触发第一次render
  28. setTimeout(() => { // 第二个异步任务
  29. a.value = a.value + 1
  30. a.value = a.value + 1
  31. console.log(a.value) // 正常打印5,触发第二次render
  32. })
  33. })
  34. }}>两层setTimeout按钮,只受异步任务的影响,正常打印,只触发两次render</button>
  35. </div>
  36. )
  37. }
  38. }
  39. })

总结

react的多个setState并发执行 区分同步和异步

  1. 正常如果受react api(react事件流、生命周期钩子、hook)控制的话,就是异步的(提高性能,只会render一次
  2. 如果不受react控制(比如js原生api:setTimeout,promise,addEventListener等),就会同步执行,render N次,性能更不好

对于vue来说,并没有这种性能问题,vue不受js原生api的影响

  1. 因为vue是对数据的变化做劫持无论是否是原生js api操作数据,数据都可以被劫持到
  2. vue会把一次同步任务内的操作,都合并起来,只render一次。 不受setTimeout,promise,addEventListener等api的影响
    多个数据并发被修改的情况,vue是同步还是异步?
  3. 数据的变更是同步的。但是render是异步的
  4. 可以理解为:一次同步任务内,无论怎么同步执行修改数据,都不会render,**最终的render会在同步任务结束后的 异步微任务中**去执行
    • vue的源码内,就是用Promise.resolve().then( 在这里面执行render )
    • 微任务的优先级比setTimeout要高,所以两层setTimeout会执行2次render

谢谢思考!

码字不易,点赞鼓励!