為什么會突然想到寫這么一個大雜燴的博文呢,必須要從筆者幾年前的一次面試說起
當時的我年輕氣盛,在簡歷上放了自己的博客地址,而面試官應(yīng)該是翻了我的博客,好幾道面試題都是圍繞著我的博文來提問
其中一個問題,直接使得空氣靜止了五分鐘,然后面試官結(jié)束了這次面試,那就是:如何手寫一個簡易的Promise對象?
在這里,我也先挖個坑,給你們五分鐘思考并自己回答一下這個問題~ (答案隱藏在文章中自行查看~)
也是自從那次面試,我告訴自己,工作實戰(zhàn)中總結(jié)的經(jīng)驗,一定要知其然知其所以然,才可以真正用好這些核心知識點,不積跬步,無以至千里
說了這么多的廢話,我們進入今天的博文正題~
目錄
什么是事件循環(huán)(Event Loop)
事件循環(huán)是JavaScript運行時環(huán)境的核心機制,用于協(xié)調(diào)事件、用戶交互、腳本、渲染、網(wǎng)絡(luò)等。
由于JavaScript是單線程的,事件循環(huán)使得它能夠執(zhí)行非阻塞操作,即使在處理IO等長時間運行的任務(wù)時也能保持響應(yīng)性。
事件循環(huán)的執(zhí)行順序
在JavaScript的執(zhí)行模型中,事件循環(huán)按照以下順序處理任務(wù):
- 執(zhí)行全局腳本代碼,這些同步代碼直接運行。
- 當執(zhí)行棧為空時,事件循環(huán)會查看微任務(wù)隊列。如果隊列中有微任務(wù),就一直執(zhí)行微任務(wù)直到隊列清空。
- 執(zhí)行一個宏任務(wù)(如由
setTimeout()
或setInterval()
設(shè)置的回調(diào))。 - 宏任務(wù)執(zhí)行完畢后,再次執(zhí)行所有微任務(wù)。
- 如果有必要,進行UI渲染。
- 開始下一輪事件循環(huán),處理下一個宏任務(wù)。
通過這種機制,JavaScript可以在單線程中有效地處理異步事件,同時保持代碼執(zhí)行的順序和預期效果。
理解這些概念將幫助你更好地設(shè)計和調(diào)試JavaScript中的異步代碼。
什么是宏任務(wù)(MacroTasks)和 微任務(wù)(MicroTasks)
宏任務(wù)
宏任務(wù)是 JavaScript 事件循環(huán)中的一個較大的任務(wù)單元,每個宏任務(wù)在執(zhí)行時會開啟一個新的事件循環(huán)
一個宏任務(wù)的完成通常會涉及到一個較為完整的工作流程,例如整個腳本的執(zhí)行、事件(如用戶交互事件)、定時器事件(setTimeout、setInterval)以及瀏覽器的 UI 渲染等
每個宏任務(wù)在執(zhí)行完畢后,會從任務(wù)隊列中清除
常見宏任務(wù)
-
setTimeout()
:用于設(shè)置定時器,在指定的時間間隔后執(zhí)行任務(wù) -
setInterval()
:用于設(shè)置定時器,在指定的時間間隔循環(huán)執(zhí)行任務(wù) -
setImmediate()
:類似setTimeout(fn, 0)
(僅在Node.js中) - IO操作:例如文件讀寫、網(wǎng)絡(luò)請求等
- UI渲染:瀏覽器需要重新渲染頁面時觸發(fā)的任務(wù)
-
requestAnimationFrame
:動畫渲染函數(shù)
拓展提問:點擊和鍵盤事件是宏任務(wù)嗎?
在 JavaScript 中,事件(如點擊和鍵盤事件) 通常被處理為任務(wù)
但它們不是宏任務(wù)(macro-tasks)也不是微任務(wù)(micro-tasks),而是作為任務(wù)隊列中的任務(wù)來處理
這些任務(wù)在宏任務(wù)和微任務(wù)之外,有自己的特殊隊列,通常稱為 任務(wù)隊列(task queue)
事件(如點擊和鍵盤事件) 通常被放入任務(wù)隊列,并且它們被視為任務(wù)的一種。當
事件循環(huán)執(zhí)行時,它會首先檢查宏任務(wù)隊列,執(zhí)行完當前宏任務(wù)后,再執(zhí)行所有的微任務(wù)。
在微任務(wù)執(zhí)行完畢后,瀏覽器可能會進行渲染操作(如果需要),然后事件循環(huán)會繼續(xù)到下一個宏任務(wù)。
因此,可以說點擊和鍵盤事件是作為任務(wù)處理的,而不特定分類為宏任務(wù)或微任務(wù)。
這種機制確保了 JavaScript 可以在單線程環(huán)境中高效地處理異步事件和操作,同時保持代碼執(zhí)行的順序性和可預測性。
微任務(wù)
微任務(wù)是在當前宏任務(wù)執(zhí)行完畢后立即執(zhí)行的任務(wù),事件循環(huán)會在每個宏任務(wù)之后執(zhí)行所有隊列中的微任務(wù)
它們的執(zhí)行時機是在下一個宏任務(wù)開始之前,當前宏任務(wù)的后續(xù)階段,微任務(wù)的執(zhí)行時間早于宏任務(wù)
微任務(wù)通常用于處理異步操作的結(jié)果,確保盡可能快地響應(yīng)
常見微任務(wù)
Promise.then/catch/finally
- Promise回調(diào):當Promise狀態(tài)改變時,會執(zhí)行相應(yīng)的回調(diào)函數(shù)
-
async
/await
:使用async函數(shù)和await關(guān)鍵字進行異步操作時,await后面的代碼會作為微任務(wù)執(zhí)行 -
process.nextTick
:在 Node.js 的事件循環(huán)的當前階段完成后、下一個事件循環(huán)階段開始之前,安排一個回調(diào)函數(shù)盡快執(zhí)行 (僅在Node.js中) -
MutaionObserver()
:瀏覽器中用于觀察DOM樹的變化,監(jiān)聽DOM變化,當DOM發(fā)生變化時觸發(fā)微任務(wù)
宏任務(wù)和微任務(wù)的區(qū)別
任務(wù)特征
- 宏任務(wù) 有明確的異步任務(wù)需要執(zhí)行和回調(diào);需要其他異步線程支持
- 微任務(wù) 沒有明確的異步任務(wù)需要執(zhí)行,只有回調(diào),不需要其他異步線程支持
存放位置
-
宏任務(wù) 中的事件放在
callback queue
中,由事件觸發(fā)線程維護 - 微任務(wù) 的事件放在微任務(wù)隊列中,由js引擎線程維護
執(zhí)行順序
- 事件循環(huán)的過程中,執(zhí)行棧在同步代碼執(zhí)行完成后,優(yōu)先檢查 微任務(wù) 隊列是否有任務(wù)需要執(zhí)行,如果沒有,再去 宏任務(wù) 隊列檢查是否有任務(wù)執(zhí)行,如此往復
- 微任務(wù) 一般在當前循環(huán)就會優(yōu)先執(zhí)行,而 宏任務(wù) 會等到下一次循環(huán)
- 因此,微任務(wù) 一般比 宏任務(wù) 先執(zhí)行
隊列數(shù)量
- 微任務(wù) 隊列只有一個
- 宏任務(wù) 隊列可能有多個
什么是 Promise
對象
在 JavaScript 中,Promise
對象是異步編程的一種重要機制,它代表了一個尚未完成但預期將來會完成的操作的最終結(jié)果。
Promise
提供了一種處理異步操作的方法,使得異步代碼易于編寫和理解。
Promise
的基本概念
Promise
對象有三種狀態(tài):
- Pending(等待中):初始狀態(tài),既不是成功,也不是失敗。
- Fulfilled(已完成):意味著操作成功完成。
- Rejected(已拒絕):意味著操作失敗或出現(xiàn)錯誤。
如何創(chuàng)建 Promise
對象
Promise
對象是通過 new Promise
構(gòu)造函數(shù)創(chuàng)建的,它接收一個執(zhí)行器函數(shù)作為參數(shù)。
這個執(zhí)行器函數(shù)本身接受兩個參數(shù):resolve
和 reject
,這兩個參數(shù)也是函數(shù)。
當異步操作成功時,調(diào)用 resolve
函數(shù);當操作失敗時,調(diào)用 reject
函數(shù)。
const myPromise = new Promise((resolve, reject) => {
// 異步操作
const condition = true; // 假設(shè)這是某種條件判斷
if (condition) {
resolve('Operation successful');
} else {
reject('Error occurred');
}
});
如何使用 Promise
對象
一旦 Promise
被解析(resolved)或拒絕(rejected),它就不能更改狀態(tài)。
你可以使用 .then()
方法來處理已完成的 Promise
,并使用 .catch()
方法來處理被拒絕的 Promise
。
還有 .finally()
方法,它在 Promise
完成后被調(diào)用,無論其結(jié)果如何。
myPromise
.then(result => {
console.log(result); // 處理結(jié)果
})
.catch(error => {
console.error(error); // 處理錯誤
})
.finally(() => {
console.log('Operation completed'); // 最終都會執(zhí)行
});
Promise
的優(yōu)勢
-
鏈式調(diào)用:
Promise
允許你通過.then()
方法鏈式調(diào)用多個異步操作,每個操作依次執(zhí)行。 -
錯誤處理:通過
.catch()
方法,可以集中處理多個異步操作中的錯誤。 -
并行處理:
Promise.all()
方法允許并行執(zhí)行多個異步操作,并等待所有操作完成。
Promise
在工作中的應(yīng)用場景
Promise
在處理如網(wǎng)絡(luò)請求、文件操作等異步操作時非常有用,它使得代碼更加清晰,減少了回調(diào)地獄(callback hell)的問題。
通過 Promise
,開發(fā)者可以寫出更加優(yōu)雅和可維護的異步代碼。
如何快速入門上手JavaScript中的 Promise
?
拓展資料 ———— 快速入門上手JavaScript中的Promise
解答文章開頭的問題:如何手寫一個簡易的 Promise
對象?
function SimplePromise(executor) {
let onResolve, onReject;
let fulfilled = false;
let rejected = false;
let called = false; // 防止resolve和reject被多次調(diào)用
let value;
let reason;
// resolve函數(shù)
function resolve(val) {
if (!called) {
value = val;
fulfilled = true;
called = true;
if (onResolve) {
onResolve(val);
}
}
}
// reject函數(shù)
function reject(err) {
if (!called) {
reason = err;
rejected = true;
called = true;
if (onReject) {
onReject(err);
}
}
}
// then方法
this.then = function(callback) {
onResolve = callback;
if (fulfilled) {
onResolve(value);
}
return this; // 支持鏈式調(diào)用
};
// catch方法
this.catch = function(callback) {
onReject = callback;
if (rejected) {
onReject(reason);
}
return this; // 支持鏈式調(diào)用
};
// 立即執(zhí)行傳入的executor函數(shù)
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 使用示例
let promise = new SimplePromise((resolve, reject) => {
setTimeout(() => {
resolve("Success!");
// reject("Error!"); // 也可以測試reject情況
}, 1000);
});
promise.then(result => {
console.log(result); // 輸出 "Success!"
}).catch(error => {
console.log(error);
});
什么是定時器函數(shù)
JavaScript 中的定時器函數(shù)允許你在一定時間后或者以指定的時間間隔重復執(zhí)行代碼。
這些功能主要通過兩個全局函數(shù)實現(xiàn):setTimeout()
和 setInterval()
。
這些函數(shù)是異步的,意味著它們不會阻塞代碼的執(zhí)行,而是在指定的延時后將任務(wù)加入到 JavaScript 的事件隊列中,等待當前執(zhí)行棧清空后再執(zhí)行。
setTimeout()
setTimeout()
函數(shù)用于在指定的毫秒數(shù)后執(zhí)行一個函數(shù)或指定的代碼。
它不會阻止后續(xù)代碼的執(zhí)行,而是在背后計時,一旦時間到達,就將回調(diào)函數(shù)加入到事件隊列中,等待執(zhí)行。
語法
let timeoutID = setTimeout(function[, delay, arg1, arg2, ...]);
-
function
:要執(zhí)行的函數(shù)。 -
delay
:延遲的時間,以毫秒為單位。如果省略,或者為 0,瀏覽器通常會有最小延遲時間(在HTML5標準中定義為4ms)。 -
arg1, arg2, ...
:傳遞給函數(shù)的額外參數(shù)。
使用示例
console.log("Hello");
setTimeout(() => {
console.log("World!");
}, 1000);
這個例子會先打印 "Hello",然后大約1秒后打印 "World!"
setInterval()
setInterval()
函數(shù)用于重復調(diào)用一個函數(shù)或執(zhí)行代碼片段,每隔指定的周期時間(以毫秒為單位)。
它也是非阻塞的,每次間隔時間到達后,就會嘗試執(zhí)行指定的代碼。
語法
let intervalID = setInterval(function[, delay, arg1, arg2, ...]);
-
function
:要定期執(zhí)行的函數(shù)。 -
delay
:執(zhí)行間隔的時間,以毫秒為單位。 -
arg1, arg2, ...
:傳遞給函數(shù)的額外參數(shù)。
使用示例
let counter = 0;
const intervalID = setInterval(() => {
console.log("Hello World!");
counter++;
if (counter === 5) {
clearInterval(intervalID);
}
}, 1000);
這個例子會每秒打印 "Hello World!",并在打印5次后停止
clearTimeout() 和 clearInterval()
這兩個函數(shù)用于取消由 setTimeout()
和 setInterval()
設(shè)置的定時器。
語法
-
clearTimeout(timeoutID)
:取消由setTimeout()
設(shè)置的定時器。 -
clearInterval(intervalID)
:取消由setInterval()
設(shè)置的定時器。
定時器函數(shù)的使用注意
雖然 setTimeout()
和 setInterval()
提供了方便的定時執(zhí)行功能,但它們并不保證精確的時間控制。
JavaScript 是單線程的,如果事件隊列中有其他任務(wù)在執(zhí)行,定時器的回調(diào)可能會延遲執(zhí)行。
此外,瀏覽器或者環(huán)境可能對這些函數(shù)的行為有特定的限制,如在后臺標簽頁或未激活的窗口中降低定時器的精度或延遲執(zhí)行,以優(yōu)化性能和電池壽命。
拓展提問:為什么要銷毀定時器?Vue中如何銷毀定時器?React中如何銷毀定時器?
在JavaScript中,銷毀定時器是一個重要的操作,主要是為了避免不必要的資源占用和潛在的內(nèi)存泄漏。定時器如果不被適當銷毀,可能會導致一些問題,如:
- 繼續(xù)執(zhí)行不必要的操作:如果定時器觸發(fā)的函數(shù)不再需要執(zhí)行,定時器仍然活躍會導致額外的計算,這可能影響程序性能。
- 內(nèi)存泄漏:在某些情況下,定時器的回調(diào)函數(shù)可能引用了外部變量或者大型數(shù)據(jù)結(jié)構(gòu),如果定時器沒有被銷毀,這些引用關(guān)系可能導致所涉及的內(nèi)存無法被垃圾回收,從而造成內(nèi)存泄漏。
Vue中銷毀定時器
在Vue中,通常我們會在組件的生命周期鉤子中設(shè)置和銷毀定時器。最常見的做法是在mounted
鉤子中創(chuàng)建定時器,并在beforeDestroy
(Vue 2.x)或beforeUnmount
(Vue 3.x)鉤子中銷毀定時器。例如:
export default {
mounted() {
this.timer = setInterval(() => {
console.log('Interval triggered');
}, 1000);
},
beforeDestroy() { // Vue 2.x
clearInterval(this.timer);
},
beforeUnmount() { // Vue 3.x
clearInterval(this.timer);
}
}
React中銷毀定時器
在React中,定時器通常在組件的生命周期方法或者鉤子中設(shè)置和清除。使用類組件時,你可以在componentDidMount
中設(shè)置定時器,并在componentWillUnmount
中清除。如果使用函數(shù)組件和Hooks,可以在useEffect
鉤子中處理定時器:
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
const timer = setInterval(() => {
console.log('Interval triggered');
}, 1000);
// 清理函數(shù)
return () => clearInterval(timer);
}, []); // 空依賴數(shù)組表示這個effect只在組件掛載時運行一次
return <div>Check the console.</div>;
}
在這個例子中,useEffect
鉤子的返回函數(shù)負責清除定時器,這個函數(shù)會在組件卸載時被調(diào)用,從而確保定時器被適當銷毀。
通過這些方法,可以確保在組件或應(yīng)用的生命周期結(jié)束時,相關(guān)的定時器也被正確清除,避免潛在的問題。
補充知識點:什么是 requestAnimationFrame
?
requestAnimationFrame
是一個由瀏覽器提供的 API,用于在下一次瀏覽器重繪之前調(diào)用特定的函數(shù),以執(zhí)行動畫或其他視覺更新。
這個函數(shù)是專門為動畫和連續(xù)的視覺更新設(shè)計的,它可以幫助你創(chuàng)建平滑的動畫效果,因為它能保證在瀏覽器進行下一次重繪之前更新動畫幀。
requestAnimationFrame
的特點
-
高效性能:
requestAnimationFrame
會將動畫函數(shù)的執(zhí)行時機安排在瀏覽器的下一次重繪之前,這樣可以保證動畫的更新和瀏覽器的繪制操作同步進行,從而減少畫面撕裂和不必要的計算和渲染,提高性能。 -
節(jié)能:相比于
setTimeout
或setInterval
,requestAnimationFrame
是更智能的,因為它會在瀏覽器標簽頁不可見時自動暫停,從而減少CPU、GPU和電力的消耗。 -
簡單的使用方式:
requestAnimationFrame
只需要一個回調(diào)函數(shù)作為參數(shù),瀏覽器會自動計算出最適合的調(diào)用時間。
requestAnimationFrame
的使用示例
假設(shè)你想要創(chuàng)建一個簡單的動畫,使一個元素在水平方向上移動:
let xPos = 0;
function animate() {
xPos += 5; // 每幀向右移動5像素
element.style.transform = `translateX(${xPos}px)`; // 更新元素位置
if (xPos < 500) { // 如果元素還沒移動到500像素的位置,繼續(xù)動畫
requestAnimationFrame(animate);
}
}
requestAnimationFrame(animate); // 開始動畫
在這個示例中,animate
函數(shù)會被連續(xù)調(diào)用,每次調(diào)用都會將元素向右移動5像素,直到它達到500像素的位置。
requestAnimationFrame
在工作中應(yīng)用的注意事項
-
requestAnimationFrame
需要在每一幀都重新調(diào)用來繼續(xù)動畫。 - 如果動畫或者視覺更新不再需要,應(yīng)當使用
cancelAnimationFrame
來取消回調(diào)函數(shù)的執(zhí)行,避免不必要的性能消耗。 - 由于
requestAnimationFrame
的調(diào)用時間是由瀏覽器決定的,通常它的頻率會與瀏覽器的刷新率相匹配,例如大多數(shù)設(shè)備上是每秒60次(即60Hz),但這可能會因設(shè)備而異。
補充知識點:什么是 setImmediate
?
setImmediate
是一個在 Node.js 環(huán)境中使用的函數(shù),用于安排一個回調(diào)函數(shù)在當前事件循環(huán)結(jié)束后、下一次事件循環(huán)開始前被立即執(zhí)行。
這個函數(shù)是特定于 Node.js 的,不是 Web 標準的一部分,因此在瀏覽器環(huán)境中不可用。
setImmediate
的功能和用途
setImmediate
的主要用途是將一些需要盡快執(zhí)行但不必阻塞當前正在執(zhí)行的操作的代碼延遲執(zhí)行。它與 setTimeout
和 process.nextTick
類似,但行為略有不同:
-
setImmediate
安排的任務(wù)會在當前事件循環(huán)的“check”階段執(zhí)行。 -
setTimeout(fn, 0)
會在定時器階段執(zhí)行,通常會有一小段延遲(最小延遲時間,通常是1毫秒,取決于環(huán)境)。 -
process.nextTick
會在當前事件循環(huán)的任何階段結(jié)束后立即執(zhí)行,甚至在進入下一個事件循環(huán)階段之前。
setImmediate
的使用示例
下面是一個簡單的 Node.js 示例,演示了 setImmediate
的用法:
console.log('開始執(zhí)行');
setImmediate(() => {
console.log('執(zhí)行 setImmediate 回調(diào)');
});
console.log('結(jié)束執(zhí)行');
在這個例子中,輸出將會是:
開始執(zhí)行
結(jié)束執(zhí)行
執(zhí)行 setImmediate 回調(diào)
這表明 setImmediate
安排的回調(diào)確實是在當前事件循環(huán)的末尾執(zhí)行的。
setImmediate
在工作中應(yīng)用的注意事項
-
非標準 API:
setImmediate
是一個非標準的 API,只在 Node.js 環(huán)境中可用。在瀏覽器中,你可能需要使用setTimeout(fn, 0)
來達到類似的效果,雖然這兩者在行為上有細微的差別。 - 使用場景:通常用于處理長時間運行的操作后需要快速響應(yīng)的場景,或者在處理完一些同步任務(wù)后需要盡快執(zhí)行的異步代碼。
補充知識點:什么是 process.nextTick
?
process.nextTick
是 Node.js 環(huán)境中的一個函數(shù),它用于在 Node.js 的事件循環(huán)的當前階段完成后、下一個事件循環(huán)階段開始之前,安排一個回調(diào)函數(shù)盡快執(zhí)行。
這意味著無論在事件循環(huán)的哪個階段調(diào)用 process.nextTick
,提供的回調(diào)函數(shù)都會在當前操作完成后立即執(zhí)行,但在任何I/O事件(包括定時器)或者執(zhí)行其他計劃任務(wù)之前執(zhí)行。
process.nextTick
的功能和用途
process.nextTick
主要用于確保在當前執(zhí)行棧運行完畢后、在進行任何異步操作之前立即處理給定的回調(diào)。
這對于處理錯誤、清理資源或者在繼續(xù)其他事件之前進行其他緊急計算是非常有用的。
與 setImmediate
的區(qū)別
盡管 process.nextTick
和 setImmediate
都用于安排異步操作,但它們的執(zhí)行時間點不同:
-
process.nextTick
回調(diào)在同一事件循環(huán)階段盡可能早地執(zhí)行,即在任何I/O事件和定時器之前。 -
setImmediate
設(shè)計為在當前事件循環(huán)的所有I/O事件處理完畢后執(zhí)行,即在下一個事件循環(huán)迭代的開始。
process.nextTick
的使用示例
下面是一個 Node.js 示例,展示了 process.nextTick
的使用:
console.log('開始執(zhí)行');
process.nextTick(() => {
console.log('執(zhí)行 process.nextTick 回調(diào)');
});
console.log('結(jié)束執(zhí)行');
在這個例子中,輸出將會是:
開始執(zhí)行
結(jié)束執(zhí)行
執(zhí)行 process.nextTick 回調(diào)
這表明 process.nextTick
安排的回調(diào)確實是在當前事件循環(huán)的末尾、在其他異步事件之前執(zhí)行的。
process.nextTick
在工作中應(yīng)用的注意事項
-
遞歸調(diào)用:如果
process.nextTick
被遞歸調(diào)用,或在一個循環(huán)中大量調(diào)用,它可以導致I/O餓死,因為它會在處理任何I/O事件之前不斷地將新的回調(diào)加入到隊列中。 -
用途選擇:
process.nextTick
非常適合在當前操作完成后立即需要運行的情況,例如在事件或低級邏輯之后立即處理錯誤或進行清理。
框架拓展:Vue 中有用到 process.nextTick
嗎?
Vue.js 中也使用了 process.nextTick
,或者更具體地說,它使用了與之類似的異步延遲功能。
process.nextTick
是 Node.js 的一個特性,但在瀏覽器環(huán)境中,Vue 使用的是 nextTick
方法。
這是 Vue 的全局 API,用于在下一個 DOM 更新循環(huán)結(jié)束后執(zhí)行延遲回調(diào)。
在內(nèi)部,Vue 會嘗試使用原生的 Promise.then
、MutationObserver
,或者 setImmediate
,最后退回到 setTimeout(fn, 0)
。
Vue中 nextTick
的應(yīng)用
-
確保 DOM 更新完成:Vue 的數(shù)據(jù)綁定和 DOM 更新是異步的。當你更改數(shù)據(jù)后,DOM 不會立刻更新。
nextTick
允許你在 DOM 更新完成后立即運行回調(diào)函數(shù),這對于 DOM 依賴的操作非常有用。 -
解決狀態(tài)更新問題:有時候,你可能在同一方法中多次更改數(shù)據(jù),使用
nextTick
可以確保所有的 DOM 更新都完成后再執(zhí)行某些操作。
Vue中 nextTick
的使用示例
new Vue({
el: '#app',
data: {
message: 'Hello'
},
methods: {
updateMessage() {
this.message = 'Updated message';
this.$nextTick(() => {
// 這個回調(diào)將在 DOM 更新后執(zhí)行
// `$nextTick()` 用來確保 `console.log('DOM updated')` 的執(zhí)行發(fā)生在 DOM 真正更新之后
console.log('DOM updated');
});
}
}
});
補充知識點:什么是 MutationObserver
?
MutationObserver
是一個強大的 Web API,用于監(jiān)視 DOM(文檔對象模型)的變化。
當 DOM 元素被添加、刪除或修改時,MutationObserver
可以被用來異步地通知這些變化,使開發(fā)者能夠響應(yīng)這些變化并執(zhí)行相應(yīng)的操作。
MutationObserver
的功能
MutationObserver
主要用于監(jiān)視以下類型的 DOM 變化:
- 子節(jié)點的添加或刪除。
- 屬性的添加、刪除或修改。
- 文本內(nèi)容的變更。
- 更多其他類型的 DOM 變化。
MutationObserver
的用途
這使得 MutationObserver
在開發(fā)復雜的 Web 應(yīng)用時非常有用,特別是在需要響應(yīng) DOM 變化來執(zhí)行某些操作的情況下,如動態(tài)內(nèi)容的加載、用戶界面的自動更新等。
如何使用 MutationObserver
要使用 MutationObserver
,你需要創(chuàng)建一個觀察者實例,定義一個回調(diào)函數(shù)來處理變化,然后指定要監(jiān)視的 DOM 節(jié)點和具體的觀察選項。
MutationObserver
的簡易示例
// 監(jiān)視目標節(jié)點
const targetNode = document.getElementById('some-id');
// 配置觀察選項:
const config = { attributes: true, childList: true, subtree: true };
// 當觀察到變動時執(zhí)行的回調(diào)函數(shù)
const callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed.');
} else if (mutation.type === 'attributes') {
console.log(`The ${mutation.attributeName} attribute was modified.`);
}
}
};
// 創(chuàng)建一個觀察者對象并傳入回調(diào)函數(shù)
const observer = new MutationObserver(callback);
// 開始觀察已配置的變動
observer.observe(targetNode, config);
// 之后,你可以停止觀察
// observer.disconnect();
MutationObserver
在工作中應(yīng)用的注意事項
-
性能考慮:雖然
MutationObserver
是異步的,但過度使用或監(jiān)視大量的 DOM 變化仍可能影響性能。合理配置觀察選項,只監(jiān)視必要的變化,可以幫助避免性能問題。 -
內(nèi)存管理:使用
MutationObserver
時應(yīng)確保在不需要時斷開觀察(使用disconnect
方法),以避免內(nèi)存泄漏。
面試問題合集
恭喜你耐心看完本文了,對照下方的問題列表,自我提問一下吧~
什么是 事件循環(huán)?
事件循環(huán) 的執(zhí)行順序是什么?
什么是 宏任務(wù)和微任務(wù)?
宏任務(wù)和微任務(wù) 有什么區(qū)別?
點擊和鍵盤事件 是宏任務(wù)嗎?
什么是Promise
對象?
如何手寫一個簡易的Promise
對象?
為什么Promise
比setTimeout
快?
Promise.all
和Promise.race
有什么區(qū)別?
什么是requestAnimationFrame
?
什么是setImmediate
?
什么是process.nextTick
?
Vue 中有用到process.nextTick
嗎?
什么是MutationObserver
?
Vue中如何銷毀定時器?React中如何銷毀定時器?為什么要銷毀定時器?
我是 fx67ll.com,如果您發(fā)現(xiàn)本文有什么錯誤,歡迎在評論區(qū)討論指正,感謝您的閱讀!
如果您喜歡這篇文章,歡迎訪問我的 本文github倉庫地址,為我點一顆Star,Thanks~ :)
轉(zhuǎn)發(fā)請注明參考文章地址,非常感謝!!!