DDR爱好者之家 Design By 杰米

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

怎么可以让页面上有从1到100显示的过程呢,就是用setTimeout或者Promise.then等方法去模拟。

讲道理,如果不在vue里,单独运行这段程序的话,输出一定是从1到100,但是为什么在vue中就不一样了呢?

for(let i=1; i<=100; i++){
	console.log(i);
}

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

JS运行机制

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

  1. 当代码执行时,所有同步的任务都在主线程上执行,形成一个执行栈;
  2. 在主线程之外还有一个任务队列(task queue),只要异步任务有了运行结果就在任务队列中放置一个事件;
  3. 一旦执行栈中所有同步任务执行完毕(主线程代码执行完毕),此时主线程不会空闲而是去读取任务队列。此时,异步的任务就结束等待的状态被执行。
  4. 主线程不断重复以上的步骤。

详解Vue的异步更新实现原理

我们把主线程执行一次的过程叫一个tick,所以nextTick就是下一个tick的意思,也就是说用nextTick的场景就是我们想在下一个tick做一些事的时候。

所有的异步任务结果都是通过任务队列来调度的。而任务分为两类:宏任务(macro task)和微任务(micro task)。它们之间的执行规则就是每个宏任务结束后都要将所有微任务清空。 常见的宏任务有setTimeout/MessageChannel/postMessage/setImmediate,微任务有MutationObsever/Promise.then

想要透彻学习事件循环,推荐Jake在JavaScript全球开发者大会的演讲,保证讲懂!

nextTick原理

派发更新

大家都知道vue的响应式的靠依赖收集和派发更新来实现的。在修改数组之后的派发更新过程,会触发setter的逻辑,执行dep.notify():

// src/core/observer/watcher.js
class Dep {
	notify() {
  	//subs是Watcher的实例数组
  	const subs = this.subs.slice()
    for(let i=0, l=subs.length; i<l; i++){
    	subs[i].update()
    }
  }
}

遍历subs里每一个Watcher实例,然后调用实例的update方法,下面我们来看看update是怎么去更新的:

class Watcher {
	update() {
  	...
  	//各种情况判断之后
    else{
    	queueWatcher(this)
    }
  }
}

update执行后又走到了queueWatcher,那就继续去看看queueWatcher干啥了(希望不要继续套娃了:

//queueWatcher 定义在 src/core/observer/scheduler.js
const queue: Array<Watcher> = []
let has: { [key: number]: "htmlcode">
const callbacks = []
let pending = false

export function nextTick (cb"htmlcode">
let timerFunc

if (typeof Promise !== 'undefined' && isNative(Promise)) {
 const p = Promise.resolve()
 timerFunc = () => {
  p.then(flushCallbacks)
  if (isIOS) setTimeout(noop)
  }
 isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
 isNative(MutationObserver) ||
 // PhantomJS and iOS 7.x
 MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
 let counter = 1
 const observer = new MutationObserver(flushCallbacks)
 const textNode = document.createTextNode(String(counter))
 observer.observe(textNode, {
  characterData: true
 })
 timerFunc = () => {
  counter = (counter + 1) % 2
  textNode.data = String(counter)
 }
 isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
 timerFunc = () => {
  setImmediate(flushCallbacks)
 }
} else {
 timerFunc = () => {
  setTimeout(flushCallbacks, 0)
 }
}

timerFunc下面一大片if else是在判断不同的设备和不同情况下选用哪种特性去实现异步任务:优先检测是否原生"htmlcode">

<div id="example">{{message}}</div>
var vm = new Vue({
 el: '#example',
 data: {
  message: '123'
 }
})
vm.message = 'new message' // 更改数据

vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
 vm.$el.textContent === 'new message' // true
})

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

以上就是详解Vue的异步更新实现原理的详细内容,更多关于vue 异步更新的资料请关注其它相关文章!

DDR爱好者之家 Design By 杰米
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
DDR爱好者之家 Design By 杰米

稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!

昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。

这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。

而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?