vue中nexttick原理(源码分析)
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了vue中nexttick原理(源码分析),小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5281字,纯文字阅读大概需要8分钟。
内容图文
![vue中nexttick原理(源码分析)](/upload/InfoBanner/zyjiaocheng/852/7768093fbebb4afb95e851ca95ab897d.jpg)
nexttick函数的作用是来延迟一个函数的执行。
结合vue nexttick.js源码进行分析:
/* @flow */
/* globals MessageChannel */
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIOS, isNative } from './env'
const callbacks = [] // 回调函数数组
let pending = false // 是否正在执行的flag
function flushCallbacks () { // 执行下一个回调
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
// Here we have async deferring wrappers using both microtasks and (macro) tasks.
// In < 2.4 we used microtasks everywhere, but there are some scenarios where
// microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690) or even between bubbling of the same
// event (#6566). However, using (macro) tasks everywhere also has subtle problems
// when state is changed right before repaint (e.g. #6813, out-in transitions).
// Here we use microtask by default, but expose a way to force (macro) task when
// needed (e.g. in event handlers attached by v-on).
// microtasks和(macro) tasks是异步延迟的包裹函数,2.4版本之前只用microtasks,但因为其在一些
// 情况下优先级过高,且可能在一些连续事件中触发,甚至是同一事件的冒泡间触发。然而都用(macro)
// task在state在重绘前改变也会存在一些问题。所以默认使用microtask,但也会再需要时强制改变为
// (macro) task
let microTimerFunc
let macroTimerFunc
let useMacroTask = false
// Determine (macro) task defer implementation.
// Technically setImmediate should be the ideal choice, but it's only available
// in IE. The only polyfill that consistently queues the callback after all DOM
// events triggered in the same loop is by using MessageChannel.
// 定义(macro) task的延迟实现,setImmediate是最优选,但只在IE中可用。所以在同一循环中所有DOM
// 事件触发后,要把回调推进同一队列中则使用MessageChannel
/* istanbul ignore if */
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { //如果有setImmediate且是原生代码则使用它来延迟
macroTimerFunc = () => {
setImmediate(flushCallbacks)
}
} else if (typeof MessageChannel !== 'undefined' && ( //其次使用MessageChannel
isNative(MessageChannel) ||
// PhantomJS
MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
const channel = new MessageChannel()
const port = channel.port2
channel.port1.onmessage = flushCallbacks
macroTimerFunc = () => {
port.postMessage(1)
}
} else { // 最后考虑使用setTimeout
/* istanbul ignore next */
macroTimerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
// Determine microtask defer implementation.
// 定义microtask延迟的实现
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise)) { //支持promise则使用它实现
const p = Promise.resolve()
microTimerFunc = () => {
p.then(flushCallbacks)
// in problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
// 在一些有问题的UIWebview中,Promise.then没有完全中断,但它可能卡在一个状态:我已经将回调
// 推进队列中但这个队列并没有刷新。直到浏览器做一些其他工作,例如处理一个计时器。所以需要
// 手动调用一个空的计数器来“强制”刷新队列
if (isIOS) setTimeout(noop)
}
} else { // 否则使用macroTimerFunc()
// fallback to macro
microTimerFunc = macroTimerFunc
}
/**
* Wrap a function so that if any code inside triggers state change,
* the changes are queued using a (macro) task instead of a microtask.
*/
export function withMacroTask (fn: Function): Function {
return fn._withTask || (fn._withTask = function () {
useMacroTask = true
const res = fn.apply(null, arguments)
useMacroTask = false
return res
})
}
// vue源码中的nexttick接收两个参数,要延迟执行的回调函数(callback),和执行该函数的指定上下文
//(context),如果没有上下文参数,则会默认为全局上下文。
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => { // 将回调函数转为数组来遍历执行
if (cb) { // 有回调则执行
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) { /没有返回context的promise
_resolve(ctx)
}
})
if (!pending) { // 如果当前没有执行回调则执行
pending = true
if (useMacroTask) { // 默认优先使用microTimerFunc()
macroTimerFunc()
} else {
microTimerFunc()
}
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
//如果么有回调,且支持promise,返回promise的resolve函数
return new Promise(resolve => {
_resolve = resolve
})
}
}
我们可以看出nexttick将需要延迟的函数放到了一个异步队列中执行,setTimeout或Promise等,来起到延迟执行的作用。
很多用途都是用于将函数放到Dom更新后执行,比如在created生命周期中拿不到dom因为还没渲染挂载到页面,这时就需要将对dom的操作放到nexttick函数中。那么为什么nexttick中的函数能延迟到dom更新完成后呢?
因为采用的是异步回调,所有异步函数都会在同步函数执行完之后在进行调用,而DOM的更新在同一事件循环中是同步的,所以能在其后执行,当然DOM操作可以放在异步函数中,但它本身是同步的。
关于异步函数的调用也有些不同,promise是在当前队列的末尾,setTimeout是在新队列的开头,所以同为异步函数,promise要比setTimeout先调用,即使setTimeout写在前面。所有的同类型异步函数也是按定义顺序执行的。
本文属于个人理解,如有错误,希望更正。
内容总结
以上是互联网集市为您收集整理的vue中nexttick原理(源码分析)全部内容,希望文章能够帮你解决vue中nexttick原理(源码分析)所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。