Để làm chủ được ReactJS thì chúng ta sẽ cần phải hiểu sâu về nguyên lý hoạt động của nó, từ đó chúng ta mới có thể hiểu được những thành phần cơ bản như vòng đời của một thành phần hay quản lý trạng thái. Trong bài này Dũng sẽ càng các bạn tìm hiểu sâu một chút về bên trong thành phần core của ReactJS nhé.
Nguyên lý hoạt động
Về cơ bản thì cũng khá phức tạp để hiểu.
Bởi vì sử dụng virtual DOM, nghĩa là không có ngay các mã HTML để tương tác mà sử dụng Javascript để tạo ra các mã HTML nên ReactJS cần duy trì một đối tượng lập lịch chạy liên tục để kiểm tra có trạng thái thay đổi hay không thì sẽ render lại nội dung HTML.
Cụ thể hơn bạn có thể clone repo https://github.com/facebook/react sau đó mở dự án vừa clone được bằng Visual Studio Code và tìm kiếm theo các từ khoá sau:
.componentDidUpdate
updateClassInstance
updateClassComponent
mountLazyComponent
beginWork()
performUnitOfWork
workLoopConcurrent
renderRootSync
renderRootConcurrent
ensureRootIsScheduled
scheduleUpdateOnFiber
updateDehydratedSuspenseComponent
updateSuspenseComponent
performConcurrentWorkOnRoot
scheduleCallback
attemptEarlyBailoutIfNoScheduledUpdate
beginWork
packages/scheduler/src/forks/Scheduler.js
yield
shouldYieldToHost
frameInterval
frameYieldMs
function advanceTimers(currentTime) {
workLoop
flushWork
shouldYield
requestHostCallback
handleTimeout
requestHostTimeout
unstable_scheduleCallback
const localSetTimeout = typeof setTimeout === 'function' ? setTimeout : null;
const localClearTimeout =
schedulePerformWorkUntilDeadline = () => {
localSetTimeout(performWorkUntilDeadline, 0);
};
Nó sẽ dẫn bạn đến tập tin Scheduler.js và đây chính là trái tim của ReactJS.
Tìm hiểu Scheduler.js
Trong mã nguồn của Scheduler.js, thời gian sleep để nhả tài nguyên cho CPU của scheduler (khung thời gian mà nó chờ trước khi tiếp tục thực hiện công việc) có thể được cấu hình và điều chỉnh thông qua các biến và hàm được sử dụng trong quá trình thực thi để tối ưu hiệu năng. Cụ thể, biến frameYieldMs
xác định thời gian mà scheduler chờ trước khi tiếp tục thực hiện công việc tiếp theo.
Dưới đây là phần mã liên quan đến việc xác định và sử dụng thời gian sleep trong scheduler:
import {
enableSchedulerDebugging,
enableProfiling,
frameYieldMs, // Đây là biến xác định thời gian sleep
userBlockingPriorityTimeout,
lowPriorityTimeout,
normalPriorityTimeout,
} from '../SchedulerFeatureFlags';
// ...
let frameInterval = frameYieldMs;
let startTime = -1;
function shouldYieldToHost(): boolean {
const timeElapsed = getCurrentTime() - startTime;
if (timeElapsed < frameInterval) {
// The main thread has only been blocked for a really short amount of time;
// smaller than a single frame. Dont yield yet.
return false;
}
// Yield now.
return true;
}
// ...
function performWorkUntilDeadline() {
if (isMessageLoopRunning) {
const currentTime = getCurrentTime();
startTime = currentTime;
let hasMoreWork = true;
try {
hasMoreWork = flushWork(currentTime);
} finally {
if (hasMoreWork) {
schedulePerformWorkUntilDeadline();
} else {
isMessageLoopRunning = false;
}
}
}
}
// ...
let schedulePerformWorkUntilDeadline;
if (typeof localSetImmediate === 'function') {
schedulePerformWorkUntilDeadline = () => {
localSetImmediate(performWorkUntilDeadline);
};
} else if (typeof MessageChannel !== 'undefined') {
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;
schedulePerformWorkUntilDeadline = () => {
port.postMessage(null);
};
} else {
schedulePerformWorkUntilDeadline = () => {
localSetTimeout(performWorkUntilDeadline, 0);
};
}
// ...
function requestHostTimeout(callback: (currentTime: number) => void, ms: number) {
taskTimeoutID = localSetTimeout(() => {
callback(getCurrentTime());
}, ms);
}
// ...
- Biến
frameYieldMs = 5
:
- Được khai báo trong
SchedulerFeatureFlags
. - Xác định khoảng thời gian mà scheduler sẽ chờ trước khi tiếp tục công việc. Đây là thời gian mà scheduler nhường quyền kiểm soát lại cho main thread để tránh việc chặn luồng này quá lâu.
- Hàm
shouldYieldToHost
:
- Sử dụng biến
frameYieldMs
để xác định xem scheduler có nên nhường quyền kiểm soát lại cho luồng chính hay không. - Nếu thời gian đã trôi qua (
timeElapsed
) ít hơnframeYieldMs
, scheduler sẽ không nhường (return false
). Nếu không, scheduler sẽ nhường (return true
).
- Hàm
performWorkUntilDeadline
:
- Thực hiện công việc cho đến khi hết thời gian cho phép (
deadline
). - Sử dụng
schedulePerformWorkUntilDeadline
để lên lịch thực hiện công việc tiếp theo nếu còn công việc cần làm.
- Cấu hình
schedulePerformWorkUntilDeadline
:
- Dựa vào môi trường (Node.js, trình duyệt cũ, môi trường DOM) để chọn phương pháp lập lịch phù hợp (
setImmediate
,MessageChannel
,setTimeout
).
Thông qua các biến và hàm này, scheduler có thể quản lý thời gian sleep giữa các khoảng thời gian làm việc để đảm bảo hiệu suất và trải nghiệm người dùng tốt nhất.
Có một điều đặc biệt là bạn không nhìn thấy hàm sleep hay delay nào trong mã nguồn điều này là do react sử dụng hàm setTimeout thay vì hàm setInterval, sleep hay delay.
Cụ thể, các cơ chế lập lịch này được thực hiện thông quarequestHostCallback
,requestHostTimeout
, vàschedulePerformWorkUntilDeadline
. Dưới đây là những phần mã quan trọng liên quan đến cơ chế này:
shouldYieldToHost
:
Hàm này xác định xem có nên nhường quyền kiểm soát lại cho luồng chính dựa trên thời gian đã trôi qua (frameInterval
) hay không.
function shouldYieldToHost(): boolean {
const timeElapsed = getCurrentTime() - startTime;
if (timeElapsed < frameInterval) {
// Main thread has only been blocked for a really short amount of time;
// smaller than a single frame. Dont yield yet.
return false;
}
// Yield now.
return true;
}
requestHostTimeout
vàcancelHostTimeout
:
Hai hàm này sử dụngsetTimeout
để lên lịch thực hiện callback sau một khoảng thời gian xác định.
function requestHostTimeout(
callback: (currentTime: number) => void,
ms: number,
) {
taskTimeoutID = localSetTimeout(() => {
callback(getCurrentTime());
}, ms);
}
function cancelHostTimeout() {
localClearTimeout(taskTimeoutID);
taskTimeoutID = ((-1: any): TimeoutID);
}
performWorkUntilDeadline
vàschedulePerformWorkUntilDeadline
:
HàmperformWorkUntilDeadline
thực hiện công việc và gọischedulePerformWorkUntilDeadline
để tiếp tục công việc khi cần thiết.
const performWorkUntilDeadline = () => {
if (isMessageLoopRunning) {
const currentTime = getCurrentTime();
startTime = currentTime;
let hasMoreWork = true;
try {
hasMoreWork = flushWork(currentTime);
} finally {
if (hasMoreWork) {
schedulePerformWorkUntilDeadline();
} else {
isMessageLoopRunning = false;
}
}
}
};
let schedulePerformWorkUntilDeadline;
if (typeof localSetImmediate === 'function') {
schedulePerformWorkUntilDeadline = () => {
localSetImmediate(performWorkUntilDeadline);
};
} else if (typeof MessageChannel !== 'undefined') {
const channel = new MessageChannel();
const port = channel.port2;
channel.port1.onmessage = performWorkUntilDeadline;
schedulePerformWorkUntilDeadline = () => {
port.postMessage(null);
};
} else {
schedulePerformWorkUntilDeadline = () => {
localSetTimeout(performWorkUntilDeadline, 0);
};
}
Tổng kết
Như vậy chúng ta đã cùng nhau tìm hiểu một chút sâu bên trong lõi của ReactJS cụ thể là nguyên lý hoạt động và lớp Scheduler.js.
Cám ơn bạn đã quan tâm đến bài viết|video này. Để nhận được thêm các kiến thức bổ ích bạn có thể:
- Đọc các bài viết của TechMaster trên facebook: https://www.facebook.com/techmastervn
- Xem các video của TechMaster qua Youtube: https://www.youtube.com/@TechMasterVietnam nếu bạn thấy video/bài viết hay bạn có thể theo dõi kênh của TechMaster để nhận được thông báo về các video mới nhất nhé.
- Chat với techmaster qua Discord: https://discord.gg/yQjRTFXb7a
Bình luận