Jak buforować obraz w JavaScript

84

Moi przyjaciele i ja pracujemy nad witryną internetową, w której chcielibyśmy buforować określone obrazy, aby wyświetlać je szybciej w przyszłości. Mam dwa główne pytania:

  1. Jak zapisujesz obraz w pamięci podręcznej?
  2. Jak używać obrazu po umieszczeniu go w pamięci podręcznej? (i tylko po to, aby sprawdzić, czy obraz jest przechowywany w pamięci podręcznej na stronie A, można go wywołać z pamięci podręcznej, aby użyć go na stronie B, prawda?)

Czy można również ustawić, kiedy wersja obrazu w pamięci podręcznej wygaśnie?

Byłoby bardzo mile widziane, gdyby został dołączony przykład i / lub link do strony, która dalej to opisuje.

W porządku używamy zarówno surowego JavaScript, jak i wersji jQuery.

Logan Besecker
źródło
12
Ty nie. Przeglądarka tak.
Pointy
czy obrazy pamięci podręcznej przeglądarki automatycznie?
Logan Besecker
8
@Logan: Tak, przeglądarka automatycznie zapisuje obrazy w pamięci podręcznej, pod warunkiem, że serwer wysyła niezbędne nagłówki, aby poinformować przeglądarkę, że można ją bezpiecznie buforować. (Te nagłówki mogą również informować przeglądarkę o czasie wygaśnięcia, co jest częścią twojego pytania.)
icktoofay
jak mogę sprawdzić, czy moja przeglądarka wysyła niezbędne nagłówki?
Logan Besecker
1
@LoganBesecker Możesz sprawdzić nagłówki odpowiedzi w sekcji Sieć narzędzi deweloperskich przeglądarki.
jlaceda

Odpowiedzi:

133

Gdy obraz zostanie w jakikolwiek sposób załadowany do przeglądarki, znajdzie się w pamięci podręcznej przeglądarki i będzie ładował się znacznie szybciej przy następnym użyciu, niezależnie od tego, czy jest to na bieżącej stronie, czy na dowolnej innej stronie, o ile obraz jest używany przed wygaśnięciem z pamięci podręcznej przeglądarki.

Aby więc wstępnie zapisać obrazy, wszystko, co musisz zrobić, to załadować je do przeglądarki. Jeśli chcesz wstępnie zapisać kilka obrazów, prawdopodobnie najlepiej zrobić to za pomocą javascript, ponieważ generalnie nie wstrzymuje ładowania strony po wykonaniu z javascript. Możesz to zrobić w ten sposób:

function preloadImages(array) {
    if (!preloadImages.list) {
        preloadImages.list = [];
    }
    var list = preloadImages.list;
    for (var i = 0; i < array.length; i++) {
        var img = new Image();
        img.onload = function() {
            var index = list.indexOf(this);
            if (index !== -1) {
                // remove image from the array once it's loaded
                // for memory consumption reasons
                list.splice(index, 1);
            }
        }
        list.push(img);
        img.src = array[i];
    }
}

preloadImages(["url1.jpg", "url2.jpg", "url3.jpg"]);

Ta funkcja może być wywoływana tyle razy, ile chcesz i za każdym razem doda ona więcej obrazów do pliku wstępnego.

Gdy obrazy zostaną wstępnie załadowane w ten sposób za pomocą javascript, przeglądarka będzie miała je w swojej pamięci podręcznej i możesz po prostu odwołać się do normalnych adresów URL w innych miejscach (na stronach internetowych), a przeglądarka pobierze ten adres URL ze swojej pamięci podręcznej, a nie przez sieć.

Z czasem pamięć podręczna przeglądarki może się zapełnić i wyrzucić najstarsze rzeczy, które nie były używane od jakiegoś czasu. W końcu obrazy zostaną opróżnione z pamięci podręcznej, ale powinny tam pozostać przez chwilę (w zależności od wielkości pamięci podręcznej i ilości innych operacji przeglądania). Za każdym razem, gdy obrazy są ponownie wstępnie ładowane lub używane na stronie internetowej, automatycznie odświeża ich pozycję w pamięci podręcznej przeglądarki, dzięki czemu jest mniej prawdopodobne, że zostaną usunięte z pamięci podręcznej.

Pamięć podręczna przeglądarki jest wielostronicowa, więc działa dla każdej strony załadowanej do przeglądarki. Możesz więc wstępnie buforować w jednym miejscu w swojej witrynie, a pamięć podręczna przeglądarki będzie działać dla wszystkich innych stron w Twojej witrynie.


Podczas wstępnego przygotowywania, jak powyżej, obrazy są ładowane asynchronicznie, więc nie będą blokować wczytywania ani wyświetlania strony. Jeśli jednak Twoja strona zawiera wiele własnych obrazów, te obrazy z pamięci wstępnej mogą konkurować o przepustowość lub połączenia z obrazami wyświetlanymi na Twojej stronie. Zwykle nie jest to zauważalny problem, ale przy wolnym połączeniu to wstępne uczenie może spowolnić ładowanie strony głównej. Jeśli wstępne ładowanie obrazów było w porządku, można użyć wersji funkcji, która będzie czekać z rozpoczęciem wstępnego ładowania do momentu, gdy wszystkie inne zasoby strony zostaną załadowane.

function preloadImages(array, waitForOtherResources, timeout) {
    var loaded = false, list = preloadImages.list, imgs = array.slice(0), t = timeout || 15*1000, timer;
    if (!preloadImages.list) {
        preloadImages.list = [];
    }
    if (!waitForOtherResources || document.readyState === 'complete') {
        loadNow();
    } else {
        window.addEventListener("load", function() {
            clearTimeout(timer);
            loadNow();
        });
        // in case window.addEventListener doesn't get called (sometimes some resource gets stuck)
        // then preload the images anyway after some timeout time
        timer = setTimeout(loadNow, t);
    }

    function loadNow() {
        if (!loaded) {
            loaded = true;
            for (var i = 0; i < imgs.length; i++) {
                var img = new Image();
                img.onload = img.onerror = img.onabort = function() {
                    var index = list.indexOf(this);
                    if (index !== -1) {
                        // remove image from the array once it's loaded
                        // for memory consumption reasons
                        list.splice(index, 1);
                    }
                }
                list.push(img);
                img.src = imgs[i];
            }
        }
    }
}

preloadImages(["url1.jpg", "url2.jpg", "url3.jpg"], true);
preloadImages(["url99.jpg", "url98.jpg"], true);
jfriend00
źródło
W tym przykładzie tworzysz nowy obiekt Image dla każdego adresu URL. Jeśli ustawisz zmienną img poza pętlą i po prostu zaktualizujesz src, powinno to mieć ten sam efekt i zmniejszyć zasoby
cadlac,
2
@cadlac - Ale aby mieć pewność, że przeglądarka faktycznie pobierze i buforuje każdy obraz, musisz poczekać, aż zakończy się pobieranie jednego obrazu przed ustawieniem nowej właściwości .src. W przeciwnym razie przeglądarka mogłaby po prostu zatrzymać wcześniejsze pobieranie i nigdy nie zapisywać go w pamięci podręcznej.
jfriend00
Wydaje się, że działa w nowszych wersjach przeglądarek bez czekania, ale masz rację. Będę musiał wypróbować to na niektórych starszych przeglądarkach, aby sprawdzić zgodność :)
cadlac
@cadlac - nie ma potrzeby. Zaktualizowałem kod, aby usunąć Image()element z listy po zakończeniu ładowania obrazu. Bezpieczniej jest poczekać, aż obraz się załaduje.
jfriend00
1
Dodałem kolejną wersję skryptu, który czeka na załadowanie innych zasobów dokumentu, dzięki czemu wstępne ładowanie obrazów nie konkuruje o przepustowość z ładowaniem widocznych zasobów strony.
jfriend00
13

jak @Pointy powiedział, że nie buforujesz obrazów za pomocą javascript, robi to przeglądarka. więc może to być to, o co prosisz, a może nie ... ale możesz wstępnie załadować obrazy za pomocą javascript. Umieszczając wszystkie obrazy, które chcesz wstępnie załadować do tablicy i umieszczając wszystkie obrazy z tej tablicy w ukrytych elementach img, skutecznie ładujesz wstępnie (lub buforujesz) obrazy.

var images = [
'/path/to/image1.png',
'/path/to/image2.png'
];

$(images).each(function() {
var image = $('<img />').attr('src', this);
});
Trav McKinney
źródło
2
czy byłoby możliwe wstępne załadowanie obrazu na jednej stronie (bez wyświetlania go), a następnie wyświetlenie go na następnej stronie?
Logan Besecker
2
O ile mi wiadomo, jeśli wstępnie załadujesz obraz na jednej stronie, zostanie on wprowadzony do pamięci podręcznej przeglądarki, a następnie będzie wyświetlany szybciej na każdej innej stronie. jeśli to źle, niech ktoś mnie poprawi.
Trav McKinney,
4

Mimo że Twoje pytanie brzmi „używając javascript”, możesz użyć prefetchatrybutu tagu linku, aby wstępnie załadować dowolny zasób. W chwili pisania tego tekstu (10 sierpnia 2016 r.) Nie jest obsługiwany w Safari, ale jest prawie wszędzie:

<link rel="prefetch" href="(url)">

Więcej informacji na temat wsparcia tutaj: http://caniuse.com/#search=prefetch

Zwróć uwagę, że IE 9,10 nie są wymienione w caniusemacierzy, ponieważ Microsoft zaprzestał ich obsługi.

Więc jeśli naprawdę utkniesz w używaniu javascript, możesz użyć jquery, aby dynamicznie dodać te elementy również do swojej strony ;-)

Brad Parks
źródło
1
Ładne ładne ładne ładne!
Bhargav Nanekalva
3

Jest kilka rzeczy, na które możesz spojrzeć:

Wstępne ładowanie obrazów
Ustawianie czasu pamięci podręcznej w pliku .htaccess
Rozmiar pliku obrazów i ich kodowanie base64.

Wstępne ładowanie: http://perishablepress.com/3-ways-preload-images-css-javascript-ajax/

Buforowanie: http://www.askapache.com/htaccess/speed-up-sites-with-htaccess-caching.html

Istnieje kilka różnych przemyśleń dotyczących kodowania base64, niektórzy twierdzą, że żądania http obciążają przepustowość, podczas gdy inni twierdzą, że „postrzegane” ładowanie jest lepsze. Zostawię to w powietrzu.

Tango Bravo
źródło
3

Dodawanie dla kompletności odpowiedzi: wstępne ładowanie z HTML

<link rel="preload" href="bg-image-wide.png" as="image">

Istnieją inne funkcje ładowania wstępnego, ale żadna z nich nie jest tak przydatna, jak <link rel="preload">:

  • <link rel="prefetch">jest obsługiwany w przeglądarkach od dłuższego czasu, ale jest przeznaczony do wstępnego pobierania zasobów, które będą używane podczas następnej nawigacji / ładowania strony (np. podczas przechodzenia do następnej strony). To jest w porządku, ale nie jest przydatne dla bieżącej strony! Ponadto przeglądarki nadają zasobom pobierania z wyprzedzeniem niższy priorytet niż te, które są wstępnie ładowane - bieżąca strona jest ważniejsza niż następna. Aby uzyskać więcej informacji, zobacz Często zadawane pytania dotyczące pobierania linków z wyprzedzeniem .
  • <link rel="prerender">renderuje określoną stronę internetową w tle, przyspieszając jej ładowanie, jeśli użytkownik przejdzie do niej. Ze względu na możliwość marnowania przepustowości użytkowników Chrome traktuje prerender jako wstępne pobieranie NoState.
  • <link rel="subresource"> był obsługiwany w Chrome jakiś czas temu i miał rozwiązać ten sam problem, co wstępne ładowanie, ale wystąpił problem: nie było sposobu, aby ustalić priorytet dla elementów (który wtedy nie istniał), więc wszystkie został pobrany z dość niskim priorytetem.

Istnieje wiele programów ładujących zasoby opartych na skryptach, ale nie mają one żadnej władzy nad kolejką priorytetów pobierania w przeglądarce i mają podobne problemy z wydajnością.

Źródło: https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content

Per Quested Aronsson
źródło
1

Mam podobną odpowiedź dla asynchronicznego wstępnego ładowania obrazów przez JS. Ładowanie ich dynamicznie przebiega tak samo, jak normalne ładowanie. będą buforować.

jeśli chodzi o buforowanie, nie możesz kontrolować przeglądarki, ale możesz to ustawić przez serwer. jeśli chcesz załadować naprawdę świeży zasób na żądanie, możesz użyć techniki omijania pamięci podręcznej, aby wymusić załadowanie nowego zasobu.

Józefa
źródło
1

Używam podobnej techniki do leniwego ładowania obrazów, ale nie mogę nie zauważyć, że JavaScript nie uzyskuje dostępu do pamięci podręcznej przeglądarki przy pierwszym ładowaniu.

Mój przykład:

Na mojej stronie głównej mam obracający się baner z 4 obrazami, suwak czeka 2 sekundy, potem javascript ładuje następny obraz, czeka 2 sekundy itd.

Te obrazy mają unikalne adresy URL, które zmieniają się za każdym razem, gdy je modyfikuję, więc otrzymują nagłówki pamięci podręcznej, które będą buforowane w przeglądarce przez rok.

max-age: 31536000, public

Teraz, kiedy otwieram Chrome Devtools i upewniam się, że opcja „Wyłącz pamięć podręczną” nie jest aktywna i ładuję stronę po raz pierwszy (po wyczyszczeniu pamięci podręcznej), wszystkie obrazy są pobierane i mają status 200. Po pełnym cyklu wszystkich obrazów w banerze żądania sieciowe zatrzymują się i używane są obrazy zapisane w pamięci podręcznej.

Teraz, gdy robię regularne odświeżanie lub przechodzę do podstrony i klikam wstecz, obrazy znajdujące się w pamięci podręcznej wydają się być ignorowane. Oczekiwałbym szarego komunikatu „z pamięci podręcznej dysku” na karcie Sieć w Chrome devtools. Zamiast tego widzę, że żądania mijają co dwie sekundy z zielonym kółkiem stanu zamiast szarego, widzę przesyłane dane, więc mam wrażenie, że pamięć podręczna nie jest w ogóle dostępna z javascript. Po prostu pobiera obraz za każdym razem, gdy strona jest ładowana.

Tak więc każde żądanie skierowane do strony głównej wyzwala 4 żądania niezależnie od zasad buforowania obrazu.

Biorąc pod uwagę powyższe razem i nowy standard http2, który obsługuje obecnie większość serwerów i przeglądarek, myślę, że lepiej jest przestać używać lazyloadingu, ponieważ http2 ładuje wszystkie obrazy prawie jednocześnie.

Jeśli to błąd w Chrome Devtools, to naprawdę zaskakuje, że nikt tego jeszcze nie zauważył. ;)

Jeśli to prawda, używanie lazyloadingu tylko zwiększa wykorzystanie pasma.

Proszę popraw mnie jeżeli się mylę. :)

Jean-Paul Ladage
źródło
1

Obecnie Google proponuje nową technikę buforowania i ulepszania procesu renderowania obrazu:

  1. Dołącz plik JavaScript Lazysizes: lazysizes.js
  2. Dodaj plik do pliku html, którego chcesz użyć: <script src="lazysizes.min.js" async></script>
  3. Dodaj lazyloadklasę do swojego obrazu: <img data-src="images/flower3.png" class="lazyload" alt="">
Mehdi Bouzidi
źródło
0

Zawsze wolę używać przykładu wymienionego w Konva JS: Image Events, aby załadować obrazy.

  1. Musisz mieć listę adresów URL obrazów jako obiekt lub tablicę, na przykład:

    var sources = { lion: '/assets/lion.png', monkey: '/assets/monkey.png' };

  2. Zdefiniuj definicję funkcji, w której otrzyma listę adresów URL obrazu i funkcję zwrotną w swojej liście argumentów, więc po zakończeniu ładowania obrazu możesz rozpocząć wykonywanie na swojej stronie internetowej:

    function loadImages(sources, callback) {
                var images = {};
                var loadedImages = 0;
                var numImages = 0;
                for (var src in sources) {
                    numImages++;
                }
                for (var src in sources) {
                    images[src] = new Image();
                    images[src].onload = function () {
                        if (++loadedImages >= numImages) {
                            callback(images);
                        }
                    };
                    images[src].src = sources[src];
                }
            }

  1. Na koniec musisz wywołać funkcję. Można to nazwać na przykład z jQuery„s Dokument gotowy

$(document).ready(function (){ loadImages(sources, buildStage); });

Mahdi Alkhatib
źródło