Учащиеся, знакомые с Vue, знают, что в Vue есть метод nextTick для асинхронного обновления данных.
Взгляните на этот каштан:
<body>
<div id="main">
<ul class="list">
<li class="item" v-for="item in list">{{ item }}</li>
</ul>
</div>
<script>
new Vue({
el: '#main',
data: {
list: [
'AAAAAAAAAA',
'BBBBBBBBBB',
'CCCCCCCCCC'
]
},
mounted: function () {
this.list.push('DDDDD')
}
})
</script>
</body>
После задания некоторых стилей страница выглядит так:
Вроде все нормально, после того как мы добавим часть данных в массив, страница действительно обновится. Однако когда мы печатаем длину li в этом элементе ul, возникает проблема: mounted: function () {
this.list.push('DDDDD')
console.log(this.$el.querySelectorAll('.item').length) // 3
}
В настоящее время, если нам нужно вычислить высоту контейнера ul через количество li для компоновки, очевидно, есть проблема. В настоящее время nextTick Vue может помочь нам решить эту проблему:
mounted: function () {
this.list.push('DDDDD')
Vue.nextTick(function() {
console.log(this.$el.querySelectorAll('.item').length) // 4
// ... 计算
})
Что касается асинхронной очереди обновлений Vue, официальный сайт говорит следующее:当你设置 vm.someData = 'new value' ,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
Проще говоря, потому что DOM будет обновлен как минимум после того, как весь код в текущем потоке будет выполнен. Следовательно, после модификации данных и после обновления DOM выполнение невозможно.Чтобы обеспечить выполнение определенного фрагмента кода после обновления DOM, этот фрагмент кода необходимо поместить в следующий цикл обработки событий, например setTimeout (fn, 0), этот блок кода будет выполнен, как только DOM будет обновлен.
Фокус: очередь, цикл событий
js — это однопоточный язык
Все мы знаем, что все задачи, выполняемые js, должны быть поставлены в очередь, а задача может быть выполнена только после выполнения задачи, стоящей перед ней. Если предыдущая задача требует много времени для вычисления, то последняя задача должна дождаться ее завершения, прежде чем ее очередь будет выполняться, что характерно для одного потока. Задачи js делятся на два типа: синхронные задачи и асинхронные задачи:
- Синхронные задачи должны выполнять задачи одну за другой по порядку, и последняя задача должна дождаться завершения своей предыдущей задачи для выполнения.
- Асинхронные задачи (например, обратные вызовы) не занимаютосновной поток, будет зажат вочередь задач, когда задача основного потока будет завершена, задачи в очереди асинхронных задач будут помещены обратно в основной поток и выполнены последовательно
Представлено на уродливой, но понятной диаграмме:
Таким образом, вывод результата выглядит так, и его легко понять:Цикл событий
Причина, по которой он называется циклом событий, заключается в том, что синхронизированные задачи могут порождать новые задачи, поэтому он постоянно ищет новые события и выполняет их. Выполнение цикла называется тиком, код, выполняемый в этом цикле, называется задачей, и весь процесс повторяется.
console.log(1);
setTimeout(()=>{
console.log(2);
},1000);
while (true){}
Приведенный выше код выводится1После этого (используйте с осторожностью! Мой браузер завис~) таймер запихивается в очередь задач, а затем основной поток продолжает выполняться вниз, встречая бесконечный цикл, так что задачи в очереди задач никогда не будут выполняться . выполнить, так что нет вывода2
очередь событий
Помимо нашего основного потока, очередь задач делится наmicrotaskиmacrotask, которые мы обычно называем микрозадачами и макрозадачами.microtaskЭтот термин является относительно новой концепцией в js, обычно мы изучаем ES6.Promiseвпервые связался.
- По приоритету выполнения задача основного потока > микрозадача > макрозадача.
- Типичные макрозадачи включают setTimeout и setInterval, а также setImmediate, который поддерживается только IE, и MessageChannel и т. д. ES6 Promise относится к микрозадачам.
console.log(1)
setTimeout(function(){
console.log(2)
})
Promise.resolve().then(function(){
console.log('promise1')
}).then(function(){
console.log('promise2')
})
console.log(4)
В соответствии с порядком выполнения можно легко получить вывод приведенного выше кода:
nextTick
Вернемся к теме выше, методу Vue nextTick.
отисходный кодНетрудно заметить, что внутри Vue пытается использовать натив для асинхронных очередей.setImmediate
Promise.then
иMessageChannel
, если текущая среда выполнения не поддерживает его, используйтеsetTimeout(fn, 0)
заменять.
Nodejs
Node изначально поддерживает егоprocess.nextTick(fn)
иsetImmediate(fn)
метод иprocess.nextTick(fn)
будет рассматриваться какmicrotask
Выполнять последовательно.