2012-07-11 19 views
8

Mã sau sử dụng Sự kiện đột biến DOM DOMNodeInserted để phát hiện sự tồn tại của phần tử body và quấn innerHTML của nó vào trình bao bọc.Các Trình theo dõi đột biến DOM có chậm hơn Sự kiện đột biến DOM không?

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
    <script> 
     function DOMmanipulation() { 
      if (document.body) { 
       document.removeEventListener('DOMNodeInserted', DOMmanipulation); 
       // DOM manipulation start 
       document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>'; 
       // DOM manipulation end 
      } 
     } 
     document.addEventListener('DOMNodeInserted', DOMmanipulation); 
    </script> 
</head> 
<body> 
    <p>Lorem ipsum dolor sit amet.</p> 
</body> 
</html> 

Và mặc dù thành công của gói, có lỗi cho thấy không tìm thấy nút. This answer của một câu hỏi giải thích rằng đó là vì khi jQuery đã được nạp, nó thêm một phần tử div vào cơ thể để thực hiện một số kiểm tra, nhưng nó không thể loại bỏ phần tử div bởi vì phần tử đó đã được gói vào trình bao bọc sao cho nó không phải là yếu tố con của cơ thể nữa.

Thí nghiệm trên cho chúng ta biết DOMNodeInserted sự kiện là nhanh hơn so với các bài kiểm tra của jQuery vì yếu tố thử nghiệm của jQuery (div) đã bọc trước khi nó có thể được loại bỏ bằng jQuery.




Bây giờ các mã sau đây có thể đạt được các thao tác tương tự, và nó sử dụng các nhà quan sát DOM Đột biến mới được giới thiệu. Tính đến thời điểm này (2012-07-11), nó chỉ hoạt động trên Chrome 18 trở lên.

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> 
    <script> 
     var observer = new WebKitMutationObserver(function() { 
      if (document.body) { 
       observer.disconnect(); 
       // DOM manipulation start 
       document.body.innerHTML = '<div class="wrapper">' + document.body.innerHTML + '</div>'; 
       // DOM manipulation end 
      } 
     }); 
     observer.observe(document, { subtree: true, childList: true }); 
    </script> 
</head> 
<body> 
    <p>Lorem ipsum dolor sit amet.</p> 
</body> 
</html> 

Mã này không tạo ra lỗi nào. Điều đó có nghĩa là jQuery nhanh hơn các Trình theo dõi đột biến DOM, do đó, nó có thể loại bỏ phần tử thử nghiệm của nó (div) trước khi phần tử đó có thể được bao bọc trong trình bao bọc.




Từ hai thí nghiệm trên, chúng ta thấy rằng khi nói đến thực hiện tốc độ:

  • DOM Đột biến Sự kiện > kiểm tra jQuery
  • kiểm tra của jQuery > Quan sát đột biến DOM

Kết quả này có thể chứng minh thích hợp rằng Trình theo dõi đột biến DOM chậm hơn Sự kiện đột biến DOM không?

Trả lời

24

Trình theo dõi đột biến DOM, không nhằm mục đích nhanh hơn Sự kiện đột biến DOM. Thay vào đó, chúng được thiết kế để có hiệu quả hơn và an toàn hơn.

Ý chính cơ bản về sự khác biệt là sự kiện đột biến DOM kích hoạt bất cứ khi nào có thay đổi. Vì vậy, mã này ví dụ sẽ tạo ra một vòng lặp gọi lại, mà cuối cùng sẽ sụp đổ trình duyệt.

document.addEventListener('DOMNodeInserted', function() { 
    var newEl = document.createElement('div'); 
    document.body.appendChild(newEl); 
}); 

Thực tế là chúng được gọi theo cách này và vì vậy thường xuyên cũng có một ảnh hưởng quan trọng trên trình duyệt, vì nó buộc một ngắt giữa các trình duyệt tính toán lại phong cách, reflow và sơn lại chu kỳ hoặc tệ hơn lực lượng trình duyệt để tính toán lại phong cách, reflow và repaint trên mỗi cuộc gọi lại. Vấn đề là tiếp tục bực mình bởi thực tế là mã khác có thể thực hiện mà làm thay đổi thêm cho DOM, mà sẽ tiếp tục bị gián đoạn bởi gọi lại của bạn.

Điều khác biệt là vì các sự kiện được truyền theo cách giống như các sự kiện DOM thông thường, bạn sẽ bắt đầu nghe những thay đổi về các yếu tố mà bạn có thể không quan tâm hoặc không giải thích trong mã của mình. Vì vậy, toàn bộ cơ chế của Sự kiện đột biến DOM có thể trở nên rắc rối để quản lý khá nhanh chóng.

Trình theo dõi đột biến DOM chống lại các vấn đề này, vì tên cho thấy quan sát các thay đổi đối với DOM và cung cấp cho bạn báo cáo về tất cả các thay đổi diễn ra từ khi bắt đầu thay đổi. Đây là một tình huống tốt hơn nhiều vì nó cho phép các trình duyệt thông báo cho bạn tại một thời điểm hợp lý, ví dụ khi tài liệu không hoạt động và tất cả các JavaScript khác có thể thực hiện các thay đổi khác đã thực hiện xong hoặc trước khi trình duyệt khởi động lại recalc/repaint cycle, do đó, nó có thể áp dụng bất kỳ thay đổi nào bạn thực hiện, mà không phải lặp lại chu kỳ ngay sau đó.

Nó cũng giúp bạn quản lý dễ dàng hơn, vì bạn có thể quét qua tất cả các yếu tố đã thay đổi để tìm những gì bạn đang tìm kiếm, thay vì viết nhiều mã xử lý trường hợp cho những thứ bạn không quan tâm, là tình huống với Sự kiện đột biến. Và quan trọng hơn là nó chỉ gọi nó một lần, vì vậy bạn không cần phải lo lắng rằng bất kỳ thay đổi nào khác sẽ ảnh hưởng đến các yếu tố tức là chúng không còn thay đổi trạng thái nữa, chúng đã thay đổi.

Vì vậy, để trả lời cho câu hỏi của bạn, Trình theo dõi đột biến DOM chậm hơn vì chúng đợi jQuery hoàn tất thao tác DOM trước khi nó thông báo cho bạn về những gì jQuery đã thay đổi. Vì lý do đã giải thích ở trên và ví dụ của bạn, chứng minh đó là giải pháp hiệu quả hơn (bạn không còn gây ra lỗi) và bạn không thực sự quan tâm rằng jQuery đã thêm một thứ gì đó vào DOM vì nó sẽ xóa nó ngay sau đó. Với các nhà quan sát bạn sẽ nhận được một báo cáo chi tiết yếu tố jQuery được thêm vào và gỡ bỏ.

Điều này vẫn còn một chút phiền hà tuy nhiên vì bạn phải tìm hiểu những gì thực sự xảy ra bằng cách kết hợp các yếu tố với tất cả các thay đổi đã diễn ra. Thực tế là khi bạn không quan tâm đến điều gì đã xảy ra (cùng một yếu tố đã được thêm vào và bị loại bỏ) thì không có gì thực sự thay đổi trong cấu trúc của DOM. Để giúp với điều này có là một thư viện nhỏ gọi MutationSummary:

http://code.google.com/p/mutation-summary/

Đó tính toán tác động ròng của những thay đổi và chỉ gọi callback của bạn đi qua trong những thay đổi đó. Vì vậy, trong trường hợp của bạn gọi lại của bạn sẽ không có được gọi là ở tất cả, bởi vì hiệu ứng ròng của sự thay đổi là số không.

Ví dụ: cho những điều sau, bạn sẽ chỉ nhận được một thay đổi. Phong cách cơ thể đã được thay đổi sang trái: 1000px. Mặc dù tôi đã thay đổi nó trong 1000 gia số. Hiệu ứng ròng của sự thay đổi chỉ là sự khác biệt giữa giá trị ban đầu của nó và giá trị cuối cùng của nó.

function moveBody() { 
    for (var i = 0; i < 1000; i++) document.body.style.left = i + 'px'; 
} 
moveBody(); 
+0

Cảm ơn rất nhiều @AshHeskes và lời giải thích tuyệt vời! Cuối cùng tôi hiểu nguyên tắc thực hiện của họ. –

6

Câu trả lời đơn giản là các nhà quan sát đột biến không đồng bộ. Chúng được gửi bất cứ khi nào động cơ cảm thấy như nó, một thời gian sau khi thay đổi đã diễn ra và trong một số trường hợp sau khi có rất nhiều thay đổi đã diễn ra. Điều đó có thể kéo dài sau khi một người nghe sự kiện đột biến DOM có thể thông báo cho bạn, nhưng trong khi đó động cơ đã được tự do hoàn thành công việc của mình mà không phải liên tục tạo và bong bóng sự kiện cho mọi thay đổi DOM.

+0

Cảm ơn Nartowicz! –