跟着做react项目(React考点 和 javaScript执行机制)

导航

  • setState()
    • 参数
    • setState()更新状态是异步还是同步的?
    • 异步的setState() 多次调用, 如何处理? (新知识)
    • 如何得到异步更新后的状态数据?
  • setState() 面试题
    • 微任务 宏任务
    • 题目
  • Component存在的问题?
  • 线程概念
    • 浏览器内核由很多模块组成:
    • 定时器
    • Web Workers

setState()

setState() 官方文档

参数

setState(updater, [callback])

参数一为带有形式参数的 updater 函数
updater 函数中接收的 state 和 props 都保证为最新。 updater 的返回值会与 state 进行浅合并。

setState(stateChange[, callback])

第一个参数除了接受函数外,还可以接受对象类型

对象方式是函数方式的简写方式
       如果新状态不依赖于原状态 ===> 使用对象方式
       如果新状态依赖于原状态 ===> 使用函数方式
       如果需要在setState()后获取最新的状态数据, 在第二个callback函数中读取

项目中,如果要更新state中数组元素,那还是新建一个数组

setState()更新状态是异步还是同步的?

1). 异步 OR 同步?

react相关回调中: 异步 ——生命周期回调、事件监听回调(不是直接使用原生dom的):利用事务来实现异步更新

其它异步回调中: 同步 ——定时器、dom事件监听回调、Promise对象(包括await 下面语句)

update2 = () => {
  setTimeout(() => {
    console.log('setTimeout setState()之前', this.state.count)
    this.setState(state => ({count: state.count + 1}))
    console.log('setTimeout setState()之后', this.state.count)
  })
}

在这里插入图片描述

update4 = () => {
  Promise.resolve().then(value => {
    console.log('Promise setState()之前', this.state.count)
    this.setState(state => ({ count: state.count + 1 }))
    console.log('Promise setState()之后', this.state.count)
  })
}

跟着做react项目(React考点 和 javaScript执行机制)_第1张图片

异步的setState() 多次调用, 如何处理? (新知识)

setState({}): 合并更新一次状态, 只调用一次render()更新界面 —状态更新和界面更新都合并了

setState(fn): 更新多次状态, 但只调用一次render()更新界面 —状态更新没有合并, 但界面更新合并了

合并更新的意思:以最后为准

使用对象形式

      update6 = () => {
        console.log('onclick setState()之前', this.state.count)
        this.setState({ count: this.state.count + 1 })
        console.log('onclick setState()之后', this.state.count)
        
        console.log('onclick setState()之前2', this.state.count)
        this.setState({ count: this.state.count + 5 })
        console.log('onclick setState()之后2', this.state.count)
      }

跟着做react项目(React考点 和 javaScript执行机制)_第2张图片
使用函数形式

      update5 = () => {
        console.log('onclick setState()之前', this.state.count)
        this.setState(state => ({ count: state.count + 1 }))
        console.log('onclick setState()之后', this.state.count)
        console.log('onclick setState()之前2', this.state.count)
        this.setState(state => ({ count: state.count + 5 }))
        console.log('onclick setState()之后2', this.state.count)
      }

跟着做react项目(React考点 和 javaScript执行机制)_第3张图片

交叉

      update7 = () => {
        console.log('onclick setState()之前2', this.state.count)
        this.setState(state => ({ count: state.count + 50 }))
        console.log('onclick setState()之后2', this.state.count)

        console.log('onclick setState()之前', this.state.count)
        this.setState({ count: this.state.count + 1 })
        console.log('onclick setState()之后', this.state.count)
      }

结果输出:

render() 1

如何得到异步更新后的状态数据?

在setState()的callback回调函数中

setState() 面试题

微任务 宏任务

JS中的宏任务和微任务的区别和用法
这篇文章介绍了为什么先promise后setTimeout。能应付做题,但完整的JavaScript线程机制,还需要阅读更详细的文章。

先理解同步任务和异步任务,(后面也会讲)

同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
当指定的事情完成时,Event Table会将这个函数移入Event Queue。
主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
上述过程会不断重复,也就是常说的Event Loop(事件循环)。
跟着做react项目(React考点 和 javaScript执行机制)_第4张图片

除了广义的同步任务和异步任务,我们对任务有更精细的定义:

宏任务一般是:包括整体代码scriptsetTimeout,setInterval、I/O、UI render。

微任务主要是:Promise.then()、Object.observe、process.nextTick。
跟着做react项目(React考点 和 javaScript执行机制)_第5张图片
跟着做react项目(React考点 和 javaScript执行机制)_第6张图片

1.Promise在前,setTimeout在后

new Promise((resolve) => {
    console.log('外层宏事件2');
    resolve()
}).then(() => {
    console.log('微事件1');
}).then(()=>{
    console.log('微事件2')
})
console.log('外层宏事件1');
setTimeout(() => {
    //执行后 回调一个宏事件
    console.log('内层宏事件3')
}, 0)
外层宏事件2
外层宏事件1
微事件1
微事件2
内层宏事件3

2.setTimeout在前,Promise在后

setTimeout(() => {
    //执行后 回调一个宏事件
    console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');

new Promise((resolve) => {
    console.log('外层宏事件2');
    resolve()
}).then(() => {
    console.log('微事件1');
}).then(()=>{
    console.log('微事件2')
})
外层宏事件1
外层宏事件2
微事件1
微事件2
内层宏事件3

题目

注释中:
第一个输出 ==> 输出值

测试之后才知道:先render(), 后执行promise微任务

    state = {
      count: 0,
    }

    componentDidMount() {
      this.setState({count: this.state.count + 1})
      this.setState({count: this.state.count + 1}) //state=1
      console.log(this.state.count) // 2 ==> 0

      this.setState(state => ({count: state.count + 1})) //state=2
      this.setState(state => ({count: state.count + 1})) //state=3
      console.log(this.state.count) // 3 ==> 0

      setTimeout(() => {
        this.setState({count: this.state.count + 1})
        console.log('timeout', this.state.count) // 10 ==> 6

        this.setState({count: this.state.count + 1})
        console.log('timeout', this.state.count) // 12 ==> 7
      }, 0)

      Promise.resolve().then(value => {
        this.setState({count: this.state.count + 1}) //state=4
        console.log('promise', this.state.count)  // 6 ==>4

        this.setState({count: this.state.count + 1}) //state=5
        console.log('promise', this.state.count) // 8 ==> 5
      })
    }

    render() {
      const count = this.state.count
      console.log('render', count)  // 1 ==> 0   4 ==>3   5 ==>4  7 ==>5  9 ==>6  11 ==>7

跟着做react项目(React考点 和 javaScript执行机制)_第7张图片

Component存在的问题?

1). 父组件重新render(), 其子组件也会重新执行render(), 即使没有任何变化

2). 当前组件setState(), 重新执行render(), 即使state没有任何变化

1). 原因: 组件的componentShouldUpdate()默认返回true, 即使数据没有变化render()都会重新执行
2). 办法1: 重写shouldComponentUpdate(), 判断如果数据有变化返回true, 否则返回false
3). 办法2: 使用PureComponent代替Component
4). 说明: 一般都使用PureComponent来优化组件性能

在这里插入图片描述

但是使用PureComponent就强制规定,你不能只是修改对象的属性值,或者是数组里面的元素,而变量的引用没有变化。
跟着做react项目(React考点 和 javaScript执行机制)_第8张图片

线程概念

跟着做react项目(React考点 和 javaScript执行机制)_第9张图片

跟着做react项目(React考点 和 javaScript执行机制)_第10张图片
在这里插入图片描述

浏览器内核由很多模块组成:

跟着做react项目(React考点 和 javaScript执行机制)_第11张图片

dom树,Element对象,存储在内存中
一个

标签,就要生成标签对应的对象
跟着做react项目(React考点 和 javaScript执行机制)_第12张图片

定时器

  document.getElementById('btn').onclick = function () {
    var start = Date.now()
    console.log('启动定时器前...')
    setTimeout(function () {
      console.log('定时器执行了', Date.now()-start)
    }, 200)
    console.log('启动定时器后...')

    // 做一个长时间的工作
    for (var i = 0; i < 1000000000; i++) {
    }
  }

在这里插入图片描述
1. 定时器真是定时执行的吗?

  • 定时器并不能保证真正定时执行
  • 一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)

2. 定时器回调函数是在分线程执行的吗?

  • 在主线程执行的, js是单线程的

3. 定时器是如何实现的?

  • 事件循环模型

在这里插入图片描述

在这里插入图片描述


  1. 代码的分类:
  • 初始化代码
  • 回调代码

js引擎执行代码的基本流程
1.先执行初始化代码: 包含一些特别的代码 (定时器,绑定事件监听)
2.后面在某个时刻才会执行回调代码

如何证明js执行是单线程的?
setTimeout()的回调函数是在主线程执行的

虽然在alert的等待确认过程中,新的浏览器是会计时的,但是回调函数依然要等待主线程代码执行完,再去执行回调函数。

setTimeout(function () {
      console.log('timeout 2222')
      alert('22222222')
    }, 5000)
    setTimeout(function () {
      console.log('timeout 1111')
      alert('1111111')
    }, 4000)
    setTimeout(function () {
      console.log('timeout() 00000')
    }, 0)
    function fn() {
      console.log('fn()')
    }
    fn()

    console.log('alert()之前')
    alert('------') //暂停当前主线程的执行, 同时计时(可能因为H5的特性), 点击确定后, 恢复程序执行。
    console.log('alert()之后')

在这里插入图片描述
跟着做react项目(React考点 和 javaScript执行机制)_第13张图片

Web Workers

跟着做react项目(React考点 和 javaScript执行机制)_第14张图片

主线程

<script type="text/javascript">
  var input = document.getElementById('number')
  document.getElementById('btn').onclick = function () {
    var number = input.value

    //创建一个Worker对象
    var worker = new Worker('worker.js')
    // 绑定接收消息的监听
    worker.onmessage = function (event) {
      console.log('主线程接收分线程返回的数据: '+event.data)
      alert(event.data)
    }

    // 向分线程发送消息
    worker.postMessage(number)
    console.log('主线程向分线程发送数据: '+number)
  }
  // console.log(this) // window

</script>

分线程

console.log(this)
this.onmessage = function (event) {
  var number = event.data
  console.log('分线程接收到主线程发送的数据: '+number)
  //计算
  var result = fibonacci(number)
  postMessage(result)
  console.log('分线程向主线程返回数据: '+result)
  // alert(result)  alert是window的方法, 在分线程不能调用
  // 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面
}

worker.js里的this不是执行window
在这里插入图片描述

postMessage发送消息
onmessage接收消息
这两个方法都是相对的。

你可能感兴趣的:(前端项目,javascript,react.js,前端)