做调查报告的网站,个人主页的设计,网站建设经费,辽源网站制作前言 - JavaScript执行原理EventLoop
JavaScript执行原理涉及到Event Loop#xff08;事件循环#xff09;的概念。JavaScript是一种单线程语言#xff0c;意味着它一次只能执行一个任务。然而#xff0c;JavaScript经常需要处理异步任务#xff0c;比如网络请求、定时器…前言 - JavaScript执行原理EventLoop
JavaScript执行原理涉及到Event Loop事件循环的概念。JavaScript是一种单线程语言意味着它一次只能执行一个任务。然而JavaScript经常需要处理异步任务比如网络请求、定时器等而这些任务的执行不能阻塞主线程。这时候就需要Event Loop来协调异步任务的执行。
Event Loop的基本工作原理
执行栈Call Stack 执行栈是存储函数调用的栈结构。当一个函数被调用它就会被推入执行栈执行完毕后就会被弹出。这是一个同步执行的过程。消息队列Message Queue 消息队列用来存放异步任务的回调函数。当异步任务完成后会将其回调函数放入消息队列。事件循环Event Loop Event Loop是一个持续运行的循环负责检查执行栈和消息队列。当执行栈为空时Event Loop会检查消息队列是否有待执行的任务。如果有就将任务的回调函数推入执行栈执行。这个过程一直循环进行形成了事件循环。宏任务Macro Task和微任务Micro Task 在事件循环中任务分为宏任务和微任务。宏任务包括整体的script代码、setTimeout、setInterval等而微任务包括Promise、process.nextTick等。微任务的优先级高于宏任务会在当前宏任务执行完毕后立即执行。
整个执行过程可以简要描述为
执行栈执行同步任务。当遇到异步任务时将其回调函数加入消息队列。当执行栈为空时Event Loop检查消息队列。如果消息队列中有任务就将任务的回调函数推入执行栈执行。重复以上过程。
下面是一个简单的例子演示Event Loop的执行过程
console.log(Start);setTimeout(function() {console.log(Timeout callback);
}, 0);Promise.resolve().then(function() {console.log(Promise callback);
});console.log(End);在上面的例子中执行顺序是
打印 ‘Start’打印 ‘End’打印 ‘Promise callback’打印 ‘Timeout callback’
这是因为Promise的回调函数属于微任务它会在当前宏任务执行完毕后立即执行而setTimeout的回调函数属于宏任务会在下一个宏任务中执行。
$nextTick的原理
Vue的nextTick其本质是对JavaScript执行原理EventLoop的一种应用。
nextTick的核心是利用了如Promise、MutationObserver、setImmediate、setTimeout等原生JavaScript方法来模拟对应的微/宏任务的实现本质是为了利用JavaScript的这些异步回调任务队列来实现Vue框架中自己的异步回调队列。
具体的解释
nextTick 是 Vue.js 中一个重要的异步更新机制它用于在 DOM 更新之后执行回调函数。Vue.js通过nextTick来确保在数据变化后DOM 已经更新。
JavaScript 的事件循环(Event Loop)分为宏任务队列macrotask queue和微任务队列microtask queue。nextTick的实现利用了这两个队列以确保在 DOM 更新后执行回调。
在 Vue.js 中当数据发生变化时Vue 异步执行 DOM 更新。在这个过程中Vue 会将需要执行的回调函数推入一个队列中而不是立即执行它们。这就是 nextTick 的核心机制。
在现代浏览器中Vue 使用 Promise 或 MutationObserver 来模拟微任务。如果两者都不可用Vue 会回退到使用 setTimeout 来模拟宏任务。
下面是一个简化的 nextTick 实现用于说明其基本原理
let callbacks [] //用于存储待执行的回调函数。
let pending false //用于标识是否已经将回调函数推入队列避免重复推入。function flushCallbacks() { //用于执行队列中的所有回调函数。pending falseconst copies callbacks.slice(0)callbacks.length 0for (let i 0; i copies.length; i) {copies[i]()}
}function nextTick(cb) { //用于将回调函数推入队列并在适当的时机执行。callbacks.push(cb)if (!pending) {pending trueif (typeof Promise ! undefined) {Promise.resolve().then(flushCallbacks)} else if (typeof MutationObserver ! undefined) {const observer new MutationObserver(flushCallbacks)const textNode document.createTextNode(1)observer.observe(textNode, { characterData: true })textNode.data 2} else if (typeof setImmediate ! undefined) {setImmediate(flushCallbacks)} else {setTimeout(flushCallbacks, 0)}}
}nextTick 在不同环境下使用了不同的异步执行机制确保了在现代浏览器和旧版本浏览器中都能够正常工作。这样Vue 能够在数据变化后等待 DOM 更新完成后执行用户传入的回调函数从而实现更可靠的异步更新机制。
$nextTick的作用
nextTick不仅是Vue内部的异步队列的调用方法同时也允许开发者在实际项目中使用这个方法来满足实际应用中对DOM更新数据时机的后续逻辑处理。
nextTick是典型的将底层JavaScript执行原理应用到具体案例中的示例引入异步更新队列机制的原因
如果是同步更新则多次对一个或多个属性赋值会频繁触发UI/DOM的渲染可以减少一些无用渲染。同时由于VirtualDOM的引入每一次状态发生变化后状态变化的信号会发送给组件组件内部使用VirtualDOM进行计算得出需要更新的具体的DOM节点然后对DOM进行更新操作。每次更新状态后的渲染过程需要更多的计算而这种无用功也将浪费更多的性能所以异步渲染变得更加至关重要。
Vue采用了数据驱动视图的思想但是在一些情况下仍然需要操作DOM。有时候可能遇到这样的情况DOM1的数据发生了变化而DOM2需要从DOM1中获取数据那这时就会发现DOM2的视图并没有更新这时就需要用到了nextTick了。
由于Vue的DOM操作是异步的所以在上面的情况中就要将DOM2获取数据的操作写在$nextTick中。
所以在以下情况下会用到nextTick
在数据变化后执行的某个操作而这个操作需要使用随数据变化而变化的DOM结构的时候这个操作就需要方法在nextTick()的回调函数中。
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleVue nextTick Example/title
/head
bodydiv idappp{{ message }}/pbutton clickchangeMessageChange Message/buttondiv v-ifshowMessageDOM Updated: {{ updatedMessage }}/div
/divscript srchttps://cdn.jsdelivr.net/npm/vue2/script
script
new Vue({el: #app,data: {message: Hello, Vue!,showMessage: false,updatedMessage: },methods: {changeMessage() {this.message Updated Message;// 使用 nextTick 确保 DOM 已经更新this.$nextTick(() {this.updatedMessage this.$el.querySelector(p).textContent;this.showMessage true;});}}
});
/script/body
/html在这个例子中当点击按钮改变message数据时我们使用this.$nextTick来确保在DOM更新之后获取更新后的文本内容并在页面上显示。
在Vue生命周期中如果在created()钩子进行DOM操作也一定要放在nextTick()的回调函数中。因为在created()钩子函数中页面的DOM还未渲染这时候也没办法操作DOM所以此时如果想要操作DOM必须将操作的代码放在nextTick()的回调函数中。
!DOCTYPE html
html langen
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0titleVue nextTick Example/title
/head
bodydiv idapp/divscript srchttps://cdn.jsdelivr.net/npm/vue2/script
script
new Vue({el: #app,created() {// 在 created 钩子中进行DOM操作使用 nextTick 确保 DOM 已经渲染this.$nextTick(() {const element document.createElement(p);element.textContent DOM Manipulation in created hook;this.$el.appendChild(element);});}
});
/script/body
/html在这个例子中我们在Vue实例的created钩子中创建一个p元素并使用this.$nextTick确保DOM已经渲染然后将这个元素添加到页面中。
源码附件点此下载