vue异步更新dom原理,vue异步改同步

  vue异步更新dom原理,vue异步改同步

  本文主要介绍vue异步更新的实现原理,帮助大家更好的理解和使用Vue。感兴趣的朋友可以了解一下。

  最近面试总是被问到这样一个问题:使用vue时,将我在for循环中声明的变量从1增加到100,然后在页面上显示I。页面上的I是从1跳到100,还是会发生什么?当然答案是只会显示100,不会有跳转过程。

  如何让页面显示从1到100的过程?就是用setTimeout或者Promise.then等方法模拟一下。

  讲道理。如果不使用vue单独运行这个程序,输出肯定是从1到100,但是为什么在vue中不一样?

  for(设I=1;i=100i ){

  console.log(一);

  }

  这涉及到Vue底层的异步更新原理,也谈到了nextTick的实现。不过在说nextTick之前,有必要先介绍一下JS的事件操作机制。

  

JS运行机制

  众所周知,JS是基于事件循环的单线程语言。实施步骤大致如下:

  代码执行时,所有同步任务都在主线程上执行,形成执行栈;

  在主线程之外还有一个任务队列。只要异步任务有运行结果,就会在任务队列中放置一个事件。

  一旦执行堆栈中的所有同步任务完成(主线程代码完成),主线程将不会空闲,而是读取任务队列。此时,异步任务已经完成,等待执行。

  主线程不断重复上述步骤。

  我们把执行主线程一次的进程叫做一个Tick,所以nextTick就是下一个Tick的意思,也就是说有下一个tick的场景就是下一个tick我们要做什么事情的时候。

  的所有异步任务结果都通过任务队列进行调度。任务分为两类:宏观任务和微观任务。它们之间的执行规则是,每个宏任务之后,所有的微任务都要清空。常见的宏观任务包括settimeout/消息通道/postmessage/setimmediate,微观任务包括MutationObsever/Promise.then

  如果想彻底研究事件周期,推荐Jake在JavaScript全球开发者大会上的演讲,一定要理解!

  

nextTick原理

  

派发更新

  众所周知,vue的响应能力依赖于收集和分发更新。修改数组后的调度更新过程会触发setter的逻辑,执行dep.notify():

  //src/core/observer/watcher.js

  类Dep

  通知(){

  //subs是Watcher实例的数组。

  const subs=this.subs.slice()

  for(设i=0,l=subs.lengthil;i ){

  分句[i]。更新()

  }

  }

  }

  在subs中遍历每个Watcher实例,然后调用实例的update方法。让我们来看看update是如何更新的:

  类监视器{

  update() {

  .

  //判断各种情况后

  否则{

  队列观察器(this)

  }

  }

  }

  更新后又去了queueWatcher,继续看queueWatcher做了什么(希望不要继续依偎了:

  //queueWatcher在src/core src/core/observer/scheduler . js中定义

  常量队列:ArrayWatcher=[]

  let has: { [key: number]:true }={}

  让等待=假

  让flushing=false

  让索引=0

  导出函数queueWatcher(watcher: Watcher) {

  const id=watcher.id

  //根据id是否重复进行优化

  if(有[id]==null){

  has[id]=true

  如果(!冲洗){

  queue.push(观察器)

  }否则{

  设i=queue.length - 1

  while(i索引队列[i]。id watcher.id){

  异

  }

  queue.splice(i 1,0,观察器)

  }

  如果(!等待){

  等待=真

  //flushSchedulerQueue函数:刷新两个队列并运行观察器

  nextTick(flushSchedulerQueue)

  }

  }

  }

  这里,在推送pushwatchers时,根据id和flushing优化队列。不是每次数据改变时触发观察器的回调,而是先将这些观察器添加到队列中,然后在nextTick之后执行flushSchedulerQueue。

  FlushSchedulerQueue函数是对保存更新事件的队列进行一些处理,使更新能够满足Vue更新的生命周期。

  这也解释了为什么for循环不能导致页面更新,因为for是主线程的代码,它会在数据更改开始时被推入队列。执行for中的代码后,如果I的值更改为100,vue将转到nextTick(flushSchedulerQueue)步骤。

  

nextTick源码

  然后打开vue2.x的源代码,目录core/util/next-tick.js,代码量很小,只有110行注释,很好理解。

  常量回调=[]

  让待定=假

  导出函数nextTick (cb?函数,ctx?对象){

  让_解决

  callbacks.push(()={

  if (cb) {

  尝试{

  cb .呼叫(ctx)

  } catch (e) {

  handleError(e,ctx, nextTick )

  }

  } else if (_resolve) {

  _解析(ctx)

  }

  })

  如果(!待定){

  待定=真

  定时器函数()

  }

  首先将传入的回调函数cb(上一节中的flushSchedulerQueue)压入回调数组,最后由timerFunc函数求解一次。

  让计时器运行

  如果(类型的承诺!==undefined 是Native(Promise)) {

  const p=Promise.resolve()

  timerFunc=()={

  p.then(刷新回调)

  if (isIOS)设置超时(noop)

  }

  isUsingMicroTask=true

  } else if(!这是一种变异观察者!==未定义 (

  isNative(变异观察器)

  //PhantomJS和iOS 7.x

  mutationobserver . tostring()===[object mutationobserver constructor]

  )) {

  让计数器=1

  const observer=new mutation observer(flush callbacks)

  const textNode=document . create textNode(String(counter))

  observer.observe(textNode,{

  characterData: true

  })

  timerFunc=()={

  计数器=(计数器1) % 2

  textNode.data=String(计数器)

  }

  isUsingMicroTask=true

  } else if (typeof setImmediate!==undefined 是Native(setImmediate)) {

  timerFunc=()={

  setImmediate(flushCallbacks)

  }

  }否则{

  timerFunc=()={

  setTimeout(flushCallbacks,0)

  }

  }

  在timerFunc下,用ielse的一大块来判断在不同设备、不同情况下,用哪些特性来实现异步任务:先检查Promise是否是原生的,再检查MutationObserver是否是原生的。如果其他方法都失败了,你只能尝试执行宏任务。首先,setImmediate是一个只有IE和Edge版本才有的功能,如果没有,它最终将被降级为setTimeout 0。

  callbacks之所以在下一个Tick中不直接执行回调函数,是为了保证下一个tick在同一个tick中多次执行,这样就不会启动多个异步任务,所有这些异步任务都压入一个同步任务中,在下一个tick中完成。

  

nextTick使用

  NextTick不仅是vue的源文件,也是vue的一个全局API。让我们来看看如何使用它。

  当设置了vm.someData=new value 时,组件不会立即重新呈现。当队列被刷新时,组件将在下一个事件循环周期被更新。在大多数情况下,我们不需要关心这个过程,但如果你想基于更新后的DOM状态做一些事情,可能会有点棘手。虽然Vue.js通常鼓励开发者以数据驱动的方式思考,避免直接接触DOM,但有时我们不得不这样做。要在数据更改后等待Vue完成更新DOM,可以在数据更改后立即使用Vue.nextTick(callback)。这样,回调函数将在DOM更新完成后被调用。

  官网用例:

  div id=示例“{message}}/div

  var vm=new Vue({

  el: #example ,

  数据:{

  消息:“123”

  }

  })

  Vm.message=新消息//更改数据

  vm。$el.textContent===新邮件//false

  Vue.nextTick(function () {

  vm。$el.textContent===新邮件//true

  })

  而且因为$nextTick()返回一个Promise对象,所以还可以使用async/await语法来处理事件,这非常方便。

  以上是对Vue异步更新实现原理的详细说明。更多关于vue异步更新的信息,请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: