vue的$nextTick的使用+源码分析
各位!久等了。
这几个月一直忙于工作,休息时间是一半追剧,一半用来去提升自己了,然后看点书玩玩基金什么的,荒废了不少时间。最近也在一直调整自己的状态,觉得还是要更新起来,把自己学到的一些东西分享给各位同学,这样对于一个技术人来说,是一件有意义的事情!
上面扯了半天白话,说了最近自己的状况,还是快点进入主题吧。
new Vue({// ...methods: {// ...example: function () {// 修改数据this.message = 'changed'// DOM 还没有更新this.$nextTick(function () {// DOM 现在更新了// `this` 绑定到当前实例this.doSomethingElse()})}}})
<template><div>{{a}}{{b}}{{c}}</div></template>
new Vue({data () {return {a: 1,b: 2,c: 3}},created () {this.a = 2this.b = 3this.c = 4}})
/*** Push a watcher into the watcher queue.* Jobs with duplicate IDs will be skipped unless it's* pushed when the queue is being flushed.*/export function queueWatcher (watcher: Watcher) {const id = watcher.idif (has[id] == null) {has[id] = trueif (!flushing) {queue.push(watcher)} else {// if already flushing, splice the watcher based on its id// if already past its id, it will be run next immediately.let i = queue.length - 1while (i > index && queue[i].id > watcher.id) {i--}queue.splice(i + 1, 0, watcher)}// queue the flushif (!waiting) {waiting = trueif (process.env.NODE_ENV !== 'production' && !config.async) {flushSchedulerQueue()return}// 这里就是延迟更新的处理 - 使用nextTick去解决频繁更新的问题。// 上面的那些代码是在做watcher收集nextTick(flushSchedulerQueue)}}}
/* @flow *//* globals MutationObserver */import { noop } from 'shared/util'import { handleError } from './error'import { isIE, isIOS, isNative } from './env'export let isUsingMicroTask = falseconst callbacks = [] // 存放收集的nextTick第一参数的回调函数let pending = false // 状态开关// 该函数将异步执行时 处理队列中收集到的所有回调 挨个执行function flushCallbacks () {pending = false// 将队列中的回调全部赋值给copiesconst copies = callbacks.slice(0)callbacks.length = 0 // 清空队列// 遍历当前的队列,挨个执行回调for (let i = 0; i < copies.length; i++) {copies[i]()}}// 定义一个 最后可拿到异步处理方式的变量let timerFunc// 如果当前环境支持promise ,那么异步的处理将使用promise去做延迟执行if (typeof Promise !== 'undefined' && isNative(Promise)) {const p = Promise.resolve() // 获得一个完成状态的promise// 将promise的异步处理函数赋值给 timerFunctimerFunc = () => {p.then(flushCallbacks) // 执行then函数,将执行收集到的调用队列处理函数执行if (isIOS) setTimeout(noop)}isUsingMicroTask = true} else if (!isIE && typeof MutationObserver !== 'undefined' &&isNative(MutationObserver) ||// PhantomJS and iOS 7.xMutationObserver.toString() === '[object MutationObserverConstructor]')) {// 环境不支持promise但支持 MutationObserver,将会用MutationObserver做为异步执行处理// (#6466 MutationObserver is unreliable in IE11)let counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {// Fallback to setImmediate.// Techinically it leverages the (macro) task queue,// but it is still a better choice than setTimeout.// 当前环境如果不支持 promise 与 MutationObserver ,如果支持setImmediate,将会使用setImmediate做为异步执行处理timerFunc = () => {setImmediate(flushCallbacks)}} else {// 如果当前环境不支持以上三种异步方式,将直接使用setTimeout定时器做为异步处理// Fallback to setTimeout.timerFunc = () => {setTimeout(flushCallbacks, 0)}}// 这个就是nextTick函数,参数两个:回调,上下文export function nextTick (cb?: Function, ctx?: Object) {let _resolve// 向队列中添加收集的回调,这里要注意,push时加了一层函数包装callbacks.push(() => {if (cb) { // 判断是否传递了 回调try {cb.call(ctx) // 通过call方法执行回调函数,并设置函数内部this指向到当前上下文} catch (e) {// 如果函数一旦执行报错,将抛错handleError(e, ctx, 'nextTick')}} else if (_resolve) {// 当不是一个回调时,执行promise的resolve方法,将状态变为完成,并传入当前上下文对象作为完成时的入参_resolve(ctx)}})if (!pending) { // 这是一个等待状态开关pending = true // 等待当前的处理完后才开始下一次的timerFunc() // 这是一个关键函数,主要是做异步操作,并且针对各种情况做了兼容的不同处理}// $flow-disable-line// 如果没有回调函数,并且当前的环境支持promise,就直接返回一个promiseif (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve})}}
微 博:前端吴佳
关注「前端技术专栏」加星标
每天给您推送最新原创技术文章
好看,帮点击在看❤️
