При работе над проектом часто используется nextTick, он используется для манипулирования DOM, просто понимается, что он выполняется после асинхронности.
Однако Vue обновляет DOM асинхронно, пока он прослушивает изменения данных, Vue открывает очередь и буферизует все изменения данных, которые происходят в одном и том же цикле.
Итак, мы должны изучить принцип выполнения nextTick.
Эту статью можно получить:
Посмотрите на долгосрочную визуальную усталость, так что, согласно небольшому пространству, о часах, вычисленных, двусторонних связывании, ответах, алгоритме разных, почему ключ не может использовать связанный основной принцип индекса, постепенно отсортирован в Последняя половина месяца, ха-ха, на самом деле организовала его часть, но я должен стать более совершенным.
Последние статьи письменные квитанции всем 👍 большое чувство выполненного долга, я буду постепенно давать вам более качественную выходную квитанцию статьи.
проблемы, возникающие на работе
В работе часто встречаются следующие проблемы, если не добавить $nextTick, это может привести к сбою получения элемента
Получить дочерний элемент
this.$nextTick(() => {
this.$refs.search.form.scheduleId = this.scheduleId;
});
默认取前面一页选取的小节数据,每个学生买的课都有小节列表
получить элемент дом
this.$nextTick(() => {
document.getElementById(`input${item.id}`).focus();
});
Потому что дом может быть не отрендерен в это время, и его нельзя получить.
Получает причину, по которой DOM записан в nextTick
为了提升性能
.因为如果在主线程中更新DOM,循环100次就要更新100次DOM;但是如果等事件循环完成之后更新DOM,只需要更新1次。其实这涉及到JS Event Loop机制。可看我的文章
★"
Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更
.如果同一个 watcher (一个属性有一个watcher)被多次触发,只会被推入到队列中一次。这种在缓冲时,会去除重复数据,对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
- То есть, когда мы получаем DOM или вызываем дочерний компонент для отображения данных, переданных родителем, Vue не сразу обновляет данные DOM, а помещает эту операцию в очередь;
- Если мы повторим это, очередь также выполнит это;
- После того, как все изменения данных, ожидающие одного и того же цикла событий, будут завершены, очередь будет обработана вне события.
Следовательно, чтобы получить DOM или вызвать дочерний компонент для отображения данных, переданных родителем, он должен использоваться в nextTick, а функция обратного вызова будет вызываться после завершения обновления DOM.
Каков конкретный принцип nextTick? Проанализируйте исходный код ниже
исходный код nextTick
Исходная ссылка: https://github.com/vuejs/vue/blob/dev/src/core/util/next-tick.js
export let isUsingMicroTask = false
const callbacks = []
let pending = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
/*
这里我们使用微任务使用异步延迟包装器。
在2.5中,我们使用了(宏)任务(与微任务结合使用)。
但是,当状态在重新绘制之前被更改时,它会有一些微妙的问题 (例如#6813,out-in transitions)。
此外,在事件处理程序中使用(宏)任务会导致一些奇怪的行为,这是无法规避的(例如#7109、#7153、#7546、#7834、#8109)。
所以我们现在到处都在使用微任务。
这种权衡的一个主要缺点是存在一些场景:
微任务的优先级过高,并在支持的顺序事件两者之间触发(例如#4521、#6690,它们有解决方案)或者甚至是在同一事件(#6566)之间冒泡。
*/
// nextTick中需要执行的函数
let timerFunc
/*
nextTick行为利用了微任务队列,可以通过Promise.then或MutationObserver访问该队列。
MutationObserver获得了更广泛的支持,但是它受到了严重的干扰,此干扰是在ios> = 9.3.3中的UIWebView触发触摸事件处理程序。触发几次后完全停止工作…因此,如果native Promise可用,我们将使用它:
*/
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
// 1.
p.then(flushCallbacks)
/*在有问题的UIWebViews中,Promise.then不会完全中断,但它会陷入一种奇怪的状态,即回调被推入微任务队列,但队列没有被刷新,直到浏览器需要做一些其他的工作,比如处理一个计时器。因此,我们可以通过添加一个空计时器来“强制”刷新微任务队列。*/
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// 2.
/*
在native Promise不可用时使用MutationObserver,
//例如PhantomJS, iOS7, android4.4
(#6466 MutationObserver在IE11中是不可靠的)
MutationObserver是用来监听目标DOM结构是否改变
*/
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)) {
// 3.
// 技术上,setImmediate利用了(宏)任务队列,但它仍然是比setTimeout更好的选择。
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// 4.
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
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) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
timerFunc()
}
// nextTick 没有cb,返回new Promise,所以nextTick不传入函数,后面可以连接then
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
Анализ кода:
-
В приведенном выше коде написано, почему бы просто не использовать Promise.then или MutationObserver, потому что это связано с проблемами совместимости.
-
1 (then) и 2 (MutationObserver) используют микрозадачи, 3 (setImmediate) и 4 (setTimeout) используют макрозадачи;
-
Можно видеть, что код Vue здесь фактически делает четыре внутренних суждения об асинхронной очереди, постоянно ухудшает текущую среду и пытается использовать родные Promise.then, MutationObserver и setImmediate, Вышеприведенные три не поддерживают окончательное использование setTimeout ;
-
Целью обработки понижения является помещение функции flushCallbacks в микрозадачи (оценка 1 и оценка 2) или макрозадачи (оценка 3 и оценка 4).
等待下一次事件循环时来执行
; -
nextTick не имеет cb и возвращает новый Promise, поэтому nextTick не передает функцию, а затем может быть подключен позже;
Прикрепил:MutationObserverMutationObserver() — это новая функция Html5, которая позволяет отслеживать изменения, внесенные в дерево DOM, и создает и возвращает новый MutationObserver, который будет вызываться при изменении указанного DOM. То есть, если nextTick будет выполнен, новый textNode в коде изменится, а если он изменится, то будет выполнена callback-функция в конструкторе MutationObserver.
Независимо от того, выполняется ли она в микрозадаче или макрозадаче, обнаруживается, что функция flushCallbacks наконец выполняется Зачем помещать ее в микрозадачу или макрозадачу для ее выполнения? Выделите этот анализ кода.
const callbacks = []
let pending = false
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
Обратные вызовы записаны после функции обратного вызова NextTick, где копия массива обратных вызовов, затем обратных вызовов как NULL, и, наконец, на массив, чтобы скопировать каждую функцию, чтобы выполнить снова; поэтому его роль только для выполнения обратных вызовов в функции обратного вызова Отказ
Суммировать
обработать:
- Поместите функцию обратного вызова в обратные вызовы и дождитесь выполнения;
- Поместите функцию выполнения в микрозадачу или макрозадачу;
- Событие зацикливается на микрозадаче или макрозадаче, а функция выполнения по очереди выполняет обратные вызовы в обратных вызовах.
разное:
- nextTick выполняется до setTimeout, тогда большинство браузеров поддерживают Promise, поэтому nextTick должен быть микрозадачей, а setTimeout — макрозадачей;
- nextTick не имеет cb и возвращает новое обещание, поэтому nextTick не передает функцию, а затем может быть подключен позже
- Если в коде используется несколько nextTick, они выполняются по порядку.
- Можно увидеть, что nextTick выполнил различную обработку совместимости для setTimeout, что также можно в широком смысле понимать как помещение функции обратного вызова в setTimeout для выполнения. Но почему бы просто не использовать setTimeout напрямую? setTimeout — макрозадача, вызываемая в следующем цикле времени.
https://juejin.cn/post/6844904147804749832