Cách thích hợp để phá hủy một đối tượng bản đồ là gì?


90

Gần đây tôi đã phát triển một ứng dụng di động html5. Ứng dụng là một trang duy nhất nơi các sự kiện thay đổi băm điều hướng thay thế toàn bộ DOM. Một phần của ứng dụng là Google Map sử dụng API v3. Trước khi div bản đồ bị xóa khỏi DOM, tôi muốn xóa mọi trình xử lý / trình nghe sự kiện và giải phóng càng nhiều bộ nhớ càng tốt vì người dùng có thể không quay lại phần đó nữa.

Cách tốt nhất để phá hủy một bản đồ là gì?


Câu hỏi liên quan (2014): stackoverflow.com/questions/21142483/…
kashiraja

Mã cho cố gắng để loại bỏ tất cả người nghe sự kiện trên bản đồ, Google maps lỗi 35821412
kashiraja

Câu trả lời:


49

Tôi đang thêm câu trả lời thứ hai cho câu hỏi này, bởi vì tôi không muốn xóa qua lại mà chúng tôi đã có thông qua các nhận xét tiếp theo về câu trả lời trước đây của tôi.

Nhưng gần đây tôi đã xem một số thông tin trực tiếp giải quyết câu hỏi của bạn và vì vậy tôi muốn chia sẻ. Tôi không biết bạn có biết về điều này hay không, nhưng trong Video Giờ làm việc của Google Maps API ngày 9 tháng 5 năm 2012 , Chris Broadfoot và Luke Mahe từ Google đã thảo luận về câu hỏi này từ stackoverflow. Nếu bạn đặt phát video thành 12:50, đó là phần họ thảo luận về câu hỏi của bạn.

Về cơ bản, họ thừa nhận rằng đó là một lỗi, nhưng cũng nói thêm rằng họ không thực sự hỗ trợ các trường hợp sử dụng liên quan đến việc tạo / phá hủy các bản đồ liên tiếp. Họ thực sự khuyên bạn nên tạo một phiên bản duy nhất của bản đồ và sử dụng lại nó trong bất kỳ trường hợp nào thuộc loại này. Họ cũng nói về việc đặt bản đồ thành null và xóa người nghe sự kiện một cách rõ ràng. Bạn đã bày tỏ mối quan tâm về người nghe sự kiện, tôi nghĩ chỉ cần đặt bản đồ thành null là đủ, nhưng có vẻ như mối quan tâm của bạn là hợp lệ, vì họ đề cập cụ thể đến người nghe sự kiện. Họ cũng khuyến nghị loại bỏ hoàn toàn DIV nắm giữ bản đồ.

Dù sao đi nữa, tôi chỉ muốn thông báo điều này và đảm bảo rằng nó được đưa vào cuộc thảo luận về stackoverflow và hy vọng nó sẽ giúp bạn và những người khác-


2
Cảm ơn - Tôi đã yêu cầu họ giải quyết câu hỏi vào giờ hành chính nhưng chưa có cơ hội xem video.
Chad Killingsworth

Vâng, bạn có thể chỉ cần cập nhật câu trả lời trước đó đề cập rằng đó là một bản cập nhật ...
TJ

4
Thật tuyệt vời .. đã là năm 2018 và dường như vẫn chưa có cách nào để làm được điều này.
JP Silvashy

28

Các câu trả lời chính thức là bạn không. Các phiên bản bản đồ trong một ứng dụng trang phải được sử dụng lại và không bị phá hủy sau đó được tạo lại.

Đối với một số ứng dụng trang đơn, điều này có nghĩa là tái cấu trúc lại giải pháp sao cho khi một bản đồ được tạo, nó có thể bị ẩn hoặc ngắt kết nối khỏi DOM, nhưng nó không bao giờ bị phá hủy / tái tạo.


Điều này rất tệ - Tôi có ứng dụng trang đơn đa ngôn ngữ và tôi muốn hiển thị Bản đồ Google trên ngôn ngữ đã chọn.
artuska

14

Vì rõ ràng bạn không thể thực sự phá hủy các phiên bản bản đồ, một cách để giảm vấn đề này nếu

  • bạn cần hiển thị nhiều bản đồ cùng một lúc trên một trang web
  • số lượng bản đồ có thể thay đổi với sự tương tác của người dùng
  • các bản đồ cần được ẩn và hiển thị lại cùng với các thành phần khác (tức là chúng không xuất hiện ở một vị trí cố định trong DOM)

đang lưu giữ một nhóm các trường hợp bản đồ. Pool theo dõi các phiên bản đang được sử dụng và khi được yêu cầu một phiên bản mới, nó sẽ kiểm tra xem có bất kỳ phiên bản bản đồ nào có sẵn là miễn phí hay không: nếu có, nó sẽ trả về một phiên bản hiện có, nếu không, nó sẽ tạo phiên bản bản đồ mới và trả lại, thêm nó vào nhóm. Bằng cách này, bạn sẽ chỉ có số lượng bản đồ tối đa bằng với số lượng bản đồ tối đa bạn từng hiển thị đồng thời trên màn hình. Tôi đang sử dụng mã này (nó yêu cầu jQuery):

var mapInstancesPool = {
 pool: [],
 used: 0,
 getInstance: function(options){
    if(mapInstancesPool.used >= mapInstancesPool.pool.length){
        mapInstancesPool.used++;
        mapInstancesPool.pool.push (mapInstancesPool.createNewInstance(options));
    } else { 
        mapInstancesPool.used++;
    }
    return mapInstancesPool.pool[mapInstancesPool.used-1];
 },
 reset: function(){
    mapInstancesPool.used = 0;
 },
 createNewInstance: function(options){
    var div = $("<div></div>").addClass("myDivClassHereForStyling");
    var map =   new google.maps.Map(div[0], options);
    return {
        map: map,
        div: div
    }
 }
}

Bạn chuyển cho nó các tùy chọn bản đồ bắt đầu (theo đối số thứ hai của hàm tạo của google.maps.Map) và nó trả về cả phiên bản bản đồ (trên đó bạn có thể gọi các hàm liên quan đến google.maps.Map) và vùng chứa, bạn có thể tạo kiểu bằng cách sử dụng lớp "myDivClassHereForStyling" và bạn có thể tự động nối thêm vào DOM. Nếu bạn cần thiết lập lại hệ thống, bạn có thể sử dụng mapInstancesPool.reset (). Nó sẽ đặt lại bộ đếm về 0, trong khi vẫn giữ tất cả các phiên bản hiện có trong nhóm để sử dụng lại. Trong ứng dụng của mình, tôi cần xóa tất cả các bản đồ cùng một lúc và tạo một bộ bản đồ mới, vì vậy không có chức năng tái chế một bản đồ cụ thể: số dặm của bạn có thể thay đổi. Để xóa bản đồ khỏi màn hình, tôi sử dụng trình tách của jQuery, không phá hủy vùng chứa của bản đồ.

Bằng cách sử dụng hệ thống này và sử dụng

google.maps.event.clearInstanceListeners(window);
google.maps.event.clearInstanceListeners(document);

và chạy

google.maps.event.clearInstanceListeners(divReference[0]);
divReference.detach()

(trong đó divReference là đối tượng jQuery của div được trả về từ Nhóm đối tượng) trên mỗi div mà tôi đang xóa, tôi đã cố gắng giữ mức sử dụng bộ nhớ của Chrome ổn định hơn hoặc ít hơn, trái ngược với việc nó tăng lên mỗi khi tôi xóa bản đồ và thêm bản đồ mới.


BẠN ĐÃ TIẾT KIỆM CUỘC ĐỜI CỦA TÔI! Cảm ơn;)
Felipe Desiderati

Vinh dự khi được giup bạn!!
Paolo Mioni

5

Tôi đã đề xuất loại bỏ nội dung của div bản đồ và sử dụng deletetrên biến giữ tham chiếu đến bản đồ và có thể nhập rõ ràng deletebất kỳ trình nghe sự kiện nào.

Tuy nhiên, có một lỗi được thừa nhận và điều này có thể không hoạt động.


Đây là một cuộc thảo luận tốt. Tôi không nghĩ rằng việc gọi điện deletesẽ bổ sung nhiều (xem stackoverflow.com/q/742623/1314132 ), nhưng nó thực sự không gây hại. Cuối cùng, nó đi xuống câu hỏi này: có bất kỳ tham chiếu nào đến đối tượng không? Nếu có, nó sẽ không được thu gom.
Sean Mickey

1
@SeanMickey: đó là nơi lỗi có liên quan. Phiên bản 2 GUnload()phải xóa tất cả các tham chiếu nội bộ của API.
Andrew Leach

Tôi đã thử nghiệm trang này trong Chrome: people.missouristate.edu/chadkillingsworth/mapsexamples/… Cho đến nay, mức sử dụng bộ nhớ sau khi bản đồ bị xóa chỉ giảm một chút, nhưng không ở gần mức trước khi bản đồ được khởi tạo.
Chad Killingsworth

@AndrewLeach Hoàn toàn có thể. Nhưng nếu họ có một lỗi gây rò rỉ bộ nhớ, chúng tôi không thể làm gì nhiều cho đến khi nó được sửa. Ý tôi là, nếu việc làm cho tất cả các đối tượng bản đồ không thể truy cập được không hoạt động, thì đó deletethực sự không phải là cách khắc phục. Họ phải sửa lỗi lớn để làm cho các tham chiếu không thể truy cập được hoạt động như bình thường hoặc thêm một chức năng mới cung cấp chức năng mà bạn mô tả GUnload().
Sean Mickey

1
Chad / Andrew: vâng, tôi đã sao chép vấn đề này, thật không may deletevà việc innerHTMLxóa bộ nhớ không hoàn toàn rõ ràng. Thật không may, nó không phải là một lỗi ưu tiên cao.
Chris Broadfoot

2

Vì google không cung cấp gunload () cho api v3, tốt hơn nên sử dụng iframe trong html và gán map.html làm nguồn cho iframe này. sau khi sử dụng làm cho src là null. Điều đó chắc chắn sẽ giải phóng bộ nhớ được sử dụng bởi bản đồ.


2
Sau đó, mỗi phiên bản iframe sẽ phải tải lại api bản đồ không phải là lý tưởng.
Chad Killingsworth

1

Khi bạn xóa div, điều đó sẽ xóa bảng hiển thị và bản đồ sẽ biến mất. Để xóa cá thể bản đồ, chỉ cần đảm bảo rằng tham chiếu của bạn tới bản đồ được đặt thành nullvà mọi tham chiếu đến các phần khác của bản đồ được đặt thành null. Tại thời điểm đó, bộ thu gom rác trong JavaScript sẽ đảm nhận việc dọn dẹp, như được mô tả trong: Thu thập rác hoạt động như thế nào trong JavaScript? .


1
Tôi không chắc rằng việc đặt biến bản đồ thành null sẽ xóa tất cả các trình xử lý sự kiện đúng cách.
Chad Killingsworth

1
Nó không chỉ là bản đồ phải được đặt thành null, mà còn bất kỳ tham chiếu nào đến bất kỳ thứ gì khác. Vì vậy, nếu tham chiếu điểm đánh dấu được đặt thành null, khiến nó không thể truy cập được , sẽ không có cách nào để tiếp cận trình nghe sự kiện. Nó có thể vẫn được kết nối với bản đồ, nhưng bản đồ không thể truy cập được, vì vậy nó chỉ là một bộ nhớ lớn về cơ bản đã trở thành mồ côi. Nó cũng giống như thiết lập một Array.length = 0; nếu không có tham chiếu nào khác đến các thành viên, chúng chỉ tạo thành một nhóm bộ nhớ mồ côi đủ điều kiện để thu gom rác.
Sean Mickey

0

Tôi đoán bạn đang nói về addEventListener. Khi bạn xóa các phần tử DOM, một số trình duyệt sẽ rò rỉ các sự kiện này và không xóa chúng. Đây là lý do tại sao jQuery thực hiện một số điều khi xóa một phần tử:

  • Nó loại bỏ các sự kiện khi nó có thể sử dụng removeEventListener. Điều đó có nghĩa là nó giữ một mảng với các trình nghe sự kiện mà nó đã thêm vào phần tử này.
  • Nó xóa các thuộc tính về sự kiện ( onclick,, onblurv.v.) sử dụng deletetrên phần tử DOM khi addEventListenerkhông có sẵn (vẫn còn, nó có một mảng lưu trữ các sự kiện được thêm vào).
  • Nó thiết lập phần tử nullđể tránh rò rỉ bộ nhớ IE 6/7/8.
  • Sau đó, nó sẽ loại bỏ phần tử.

Tôi chủ yếu đề cập đến các sự kiện API Google Maps nội bộ. Chúng có thể được thêm / xóa / kích hoạt bằng cách sử dụng các phương thức sự kiện API được ghi lại tại Develop.google.com/maps/documentation/javascript/… . Mặc dù có chức năng tương tự như addEventListener của trình duyệt, nhưng có một số lượng lớn các sự kiện tùy chỉnh dành riêng cho bản đồ (chẳng hạn như "bounds_changed" và một số trình xử lý sự kiện đó nối vào các sự kiện của trình duyệt, chẳng hạn như sự kiện "thay đổi kích thước" bản đồ.
Chad Killingsworth

Sau đó, giữ một mảng các sự kiện được thêm vào và loại bỏ rồi sử dụng thủ công removeEventListenerhoặc deletetùy thuộc vào loại sự kiện.
Florian Margaine
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.