Предпосылка nextTick
Поскольку Vue заставляет представление обновлять данные асинхронно, то есть когда мы изменяем данные в событии, представление не будет обновляться немедленно, а будет обновлено после того, как все изменения данных в том же цикле событий будут завершены. Подобно механизму цикла событий Event Loop.
Официальное введение
Во-первых, давайте взглянем на введение, данное на официальном сайте:
Vue.nextTick([callback, context])
-
параметр:
- {Function} [callback]
- {Object} [context]
-
Применение:
в следующий разЦикл обновления DOMВыполнить отложенный обратный вызов после окончания. Используйте этот метод сразу после изменения данных, чтобы получить обновленную модель DOM.
// 修改数据
vm.msg = 'Hello'
// 当我们在这里调用DOM的数据时,它其实还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
// 2.1.0新增 Promise用法
Vue.nextTick()
.then(function () {
// 此时DOM已经更新
})
Новое с версии 2.1.0: возвращает обещание, если обратный вызов не предоставлен и в среде, поддерживающей обещание. Обратите внимание, что Vue не поставляется с полифиллом для промисов, поэтому, если ваш целевой браузер изначально не поддерживает промисы (IE: что вы, ребята, делаете), вам придется предоставить свой собственный полифилл.
Цикл обновления DOM
Прежде всего, Vue реализует отзывчивость не обновлять DOM сразу после изменения данных, но для выполнения обновлений DOM асинхронно после всех изменений данных в контуре события.
О циклах асинхронных и событий вы можете посмотреть на статью, которую я написал раньше.говорить об асинхронности
Если вы не хотите вдаваться в подробности, то здесь я кратко изложу цикл обработки событий:
Синхронное выполнение кода => Найдите асинхронную очередь, войдите в стек выполнения, выполните Callback1[цикл событий 1] => Найдите асинхронную очередь, войдите в стек выполнения, выполните Callback2[цикл событий 2] => .....
То есть каждый асинхронный обратный вызов будет независимо формировать цикл обработки событий.
Таким образом, мы можем выйти из времени триггера nextTick.
После выполнения кода в цикле событий => обновление DOM => вызов обратного вызова nextTick => вход в следующий цикл
Пример дисплея
Говорить дешево, покажи мне код - Линус Торвальдс
Может быть невозможно получить четкое представление о механизме nextTick только с некоторыми концептуальными объяснениями.Давайте посмотрим на предыдущий пример.
<template>
<div class="app">
<div ref="contentDiv">{{content}}</div>
<div>在nextTick执行前获取内容:{{content1}}</div>
<div>在nextTick执行之后获取内容:{{content2}}</div>
<div>在nextTick执行前获取内容:{{content3}}</div>
</div>
</template>
<script>
export default {
name:'App',
data: {
content: 'Before NextTick',
content1: '',
content2: '',
content3: ''
},
methods: {
changeContent () {
this.content = 'After NextTick' // 在此处更新content的数据
this.content1 = this.$refs.contentDiv.innerHTML //获取DOM中的数据
this.$nextTick(() => {
// 在nextTick的回调中获取DOM中的数据
this.content2 = this.$refs.contentDiv.innerHTML
})
this.content3 = this.$refs.contentDiv.innerHTML
}
},
mount () {
this.changeContent()
}
}
</script>
Когда мы открываем страницу, мы видим, что результат:
After NextTick
在nextTick执行前获取内容:Before NextTick
在nextTick执行之后获取内容:After NextTick
在nextTick执行前获取内容:Before NextTick
Таким образом, мы можем знать, что хотяcontent1а такжеcontent3Заявление для получения содержимого написано вcontentПосле выписки изменений данных, но они принадлежат к одному и тому же контуре события, такcontent1а такжеcontent3По-прежнему получите «Before NextTick» иcontent2Оператор для получения содержимого записывается в обратном вызове nextTick и выполняется после обновления DOM, поэтому получается обновленный «After NextTick».
Сценарии применения
Ниже приведены некоторые из основных сценариев применения nextTick.
Выполнять манипуляции с DOM в течение созданного жизненного цикла
когда вcreated()Не рекомендуется выполнять манипуляции с DOM непосредственно во время жизненного цикла, потому что DOM в это время не обрабатывается. Таким образом, решение состоит в том, чтобы записать манипуляцию с DOM вVue.nextTick()в функции обратного вызова. Или поставить операциюmounted()в функции хука
После изменения данных требуются операции на основе структуры DOM.
После обновления данных, если есть еще операции, которые необходимо выполнить в соответствии со структурой DOM после обновления данных, мы должны поместить эту часть операций в функцию обратного вызова **Vue.nextTick()**
Подробная причина этой части очень четко объясняется в официальной документации Vue:
Может быть, ты не заметил, ВьюасинхронныйВыполните обновление DOM. Как только наблюдается изменение данных, Vue открывает очередь и буферизует все изменения данных, которые происходят в том же цикле событий. Если один и тот же наблюдатель запускается несколько раз, он будет помещен в очередь только один раз. Эта дедупликация во время буферизации важна, чтобы избежать ненужных вычислений и манипуляций с DOM. Затем, в следующем цикле событий «тик», Vue очищает очередь и выполняет фактическую (дедублированную) работу. Vue внутренне пытается использовать натив для асинхронных очередей
Promise.thenа такжеMessageChannel, если среда выполнения не поддерживает это, используйтеsetTimeout(fn, 0)заменять.Например, когда вы устанавливаете
vm.someData = 'new value', компонент не будет повторно отображаться немедленно. Когда очередь очищается, компонент обновляется на следующем «тике», когда очередь цикла событий пуста. В большинстве случаев нам не нужно заботиться об этом процессе, но если вы хотите что-то сделать после обновления состояния DOM, это может быть немного сложно. В то время как Vue.js обычно поощряет разработчиков мыслить «управляемыми данными» и избегать прямого контакта с DOM, иногда мы так и поступаем. Чтобы дождаться, пока Vue завершит обновление DOM после изменения данных, вы можете использовать сразу после изменения данныхVue.nextTick(callback). Таким образом, функция обратного вызова будет вызываться после завершения обновления DOM.
Приложение: анализ исходного кода nextTick
Персональный перевод, если что-то не так, пожалуйста, не стесняйтесь спрашивать
export const nextTick = (function () {
// 存放所有的回调函数
const callbacks = []
// 是否正在执行回调函数的标志
let pending = false
// 触发执行回调函数
let timerFunc
// 处理回调函数
function nextTickHandler () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
// 执行回调函数
copies[i]()
}
}
// nextTick行为利用了微任务队列
// 它可以通过原生Promise或者MutationObserver实现
// MutationObserver已经有了广泛的浏览器支持,然而他仍然在UIWebView在ios系统9.3.3以上的
// 系统有严重的Bug,问题发生在我们触摸事件的触发时。
// 它会在我们触发一段时间后完全停止,所以原生Promise是有效可以利用的,我们会使用它:
/* istanbul ignore if */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve()
var logError = err => { console.error(err) }
timerFunc = () => {
p.then(nextTickHandler).catch(logError)
// 在有问题的 UIWebViews 中,Promise.then 方法不会完全的停止,但它可能会在一个
// 奇怪的状态卡住当我们把回调函数推入一个微任务队列但是这个队列并不是在冲洗中,知道
// 浏览器需要做一些其他的任务时,例如:执行一个定时函数。因此我们可以"强制"微任务队
// 列被冲洗通过加入一个空的定时函数
if (isIOS) setTimeout(noop)
}
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// 使用MutationObserver当Promise不可用时,
// 例如 PhantomJS, iOS7, Android 4.4
var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
} else {
// 当MutationObserver 和 Promise都不可以使用时
// 我们使用setTimeOut来实现
/* istanbul ignore next */
timerFunc = () => {
setTimeout(nextTickHandler, 0)
}
}
return function queueNextTick (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()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise((resolve, reject) => {
_resolve = resolve
})
}
}
})()
Мы можем знать из исходного кода,timeFuncЭта функция играет роль отложенного исполнения и имеет три реализации
- Promise
- MutationObserver
- setTimeout
вPromiseа такжеsetTimeoutМы не незнакомы, давайте сосредоточимся на следующемMutationObserver
MutationObserverЭто новый API в HTML5, который используется для отслеживания изменений DOM. Он может отслеживать удаление дочерних узлов, изменение атрибутов, изменение текстового содержимого и т. д. в объекте DOM.
Процесс вызова прост, но немного необычен: сначала нужно привязать к нему callback:
let mo = new MutationObserver(callback)
ДаваяMutationObserverКонструктор прохода в обратном вызове можно получитьMutationObserverнапример, этот обратный вызов будет вызван вMutationObserverЗапускается, когда экземпляр ожидает изменения.
на этот раз ты просто даешьMutationObserverЭкземпляр привязан к обратному вызову.Еще не установлено, какой DOM отслеживать, следить за удалением узла или следить за изменением атрибута. и позвони егоobserverспособ сделать это:
var domTarget = 你想要监听的dom节点
mo.observe(domTarget, {
characterData: true //说明监听文本内容的修改。
})
существуетnextTickсерединаMutationObserverЭффект показан на рисунке ниже. После прослушивания обновления DOM вызывается функция обратного вызова.
Суммировать
- В том же цикле событий nextTick вызывается только после выполнения всех синхронных обновлений данных.
- DOM не начнет рендеринг, пока данные в среде синхронного выполнения не будут полностью обновлены.
- В том же цикле событий, если есть несколько nextTick, они будут вызываться в исходном порядке выполнения.
- После выполнения каждой асинхронной функции обратного вызова она будет существовать в независимом цикле обработки событий, соответствующем ее собственному независимому nextTick.
- Просмотрите реализацию обновления vue DOM с помощью ES6.
Promiseи HTML5MutationObserver, когда среда не поддерживает это, используйтеsetTimeout(fn, 0)альтернатива. Все три вышеуказанных метода являются асинхронными API. вMutationObserverПодобные события отличаются, событие срабатывает синхронно, которое срабатывает асинхронно, то есть после изменения DOM оно срабатывает не сразу, а срабатывает после завершения всех текущих операций с DOM.
Ссылка на ссылку
Официальная документация Vue — асинхронная очередь обновлений
Рухэн: Простое понимание nextTick в Vue
Личный гитхаб:Reaper622
Добро пожаловать, чтобы учиться и обмениваться