Việc thay đổi DOM, hay nói cách khác là thay đổi HTML, thường được thực hiện bằng cách chỉnh sửa thuộc tính innerHTML của một element. Tuy nhiên, phương pháp này không hiệu quả về mặt hiệu suất khi cần cập nhật giao diện người dùng thường xuyên. Nguyên nhân là do innerHTML phải phân tích cú pháp các nút DOM từ một chuỗi, xử lý trước và sau đó mới gắn nó vào DOM thật. Nếu có quá nhiều thay đổi HTML trên một trang web, hiệu suất sẽ bị ảnh hưởng đáng kể.
Để giải quyết vấn đề này, DOM ảo ra đời. DOM ảo hoạt động như một bản sao của DOM thật được lưu trữ trong bộ nhớ. Khi người dùng tương tác với trang web và trạng thái của trang web được cập nhật, một DOM ảo mới sẽ được tạo ra. DOM ảo mới này sau đó được so sánh với DOM ảo trước đó để xác định những thay đổi. Cuối cùng, chỉ những thay đổi này mới được áp dụng lên DOM thật, giúp tối ưu hóa hiệu suất render và mang lại trải nghiệm mượt mà hơn cho người dùng.
Giờ chúng ta thử bắt đầu bằng 1 ví dụ cơ bản:
<!DOCTYPE html>
<html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document object model</title>
</head>
<body> <div class="card" style="width: 18rem;"> <img class="card-img-top" alt="Card image cap"> <div class="card-body"> <h5 class="card-title">Lorem, ipsum dolor.</h5> <p class="card-text">Lorem ipsum dolor sit amet consectetur adipisicing elit. Hic, tenetur!</p> <a href="#" class="btn btn-primary">Lorem, ipsum.</a> </div> </div> <script src="/script.js"></script>
</body>
</html>
console.log(document)
Output khi này sẽ là:
Chúng ta có thể truy cập dom bằng console.log(document) . Khi chúng ta kiểm tra đối tượng Document, chúng ta hiểu rằng đối tượng này biểu diễn một tài liệu HTML. Nói cách khác, đối tượng document chứa tất cả các thẻ trong một tài liệu HTML .
Với Javascript, chúng ta có thể truy cập các thẻ bên trong đối tượng tài liệu và thao tác các thẻ bên trong đối tượng tài liệu này để tạo các trang web động. Chúng ta hãy đưa ra một ví dụ về cách truy cập:
const wrapper = document.getElementById("card") console.log(wrapper)
Output khi này sẽ là:
Bây giờ chúng ta biết rằng cách dễ nhất để thay đổi dom ("Thay đổi HTML") là thay đổi thuộc tính innerHTML trong một phần tử. Phương pháp sửa đổi html này không hoạt động tốt trong việc vẽ lại DOM (" Cập nhật những gì người dùng nhìn thấy "). Điều này là do innerHTML cần phân tích cú pháp các nút DOM từ một chuỗi, xử lý trước và thêm vào. Nếu có quá nhiều đột biến html trên một trang web, sẽ có vấn đề về hiệu suất.
Hầu hết các thư viện JavaScript hiện đại đều sử dụng DOM ảo và thuật toán "diff" để so sánh các DOM ảo và xác định những thay đổi cần cập nhật lên DOM thật. Mặc dù DOM ảo giúp cải thiện hiệu suất đáng kể, vẫn cần lưu ý một số điểm để tối ưu hóa hơn nữa. Ví dụ, một thay đổi nhỏ trong DOM ảo có thể dẫn đến việc render lại toàn bộ trang web. Do đó, các thư viện thường cung cấp các "hook" để tối ưu hóa hiệu suất trong những trường hợp cụ thể.