Czy ostatnio w Konsoli deweloperów w Chrome pojawiło się takie ostrzeżenie?
(index):34 A Parser-blocking, cross-origin script,
https://paul.kinlan.me/ad-inject.js, is invoked via document.write().
This may be blocked by the browser if the device has poor network connectivity.
Złożoność jest jedną z wielkich zalet internetu, ponieważ umożliwia nam łatwą integrację z usługami innych firm, co pozwala tworzyć świetne nowe produkty. Jednym z negatywnych aspektów modułowości jest to, że zakłada ona współodpowiedzialność za wrażenia użytkownika. Jeśli integracja nie jest optymalna, wrażenia użytkownika mogą być negatywnie wpływione.
Jedną z znanych przyczyn słabej skuteczności jest używanie document.write()
na stronach,
zwłaszcza w przypadku skryptów. Chociaż wygląda to niewinnie, może to spowodować poważne problemy dla użytkowników.
document.write('<script src="https://example.com/ad-inject.js"></script>');
Zanim przeglądarka będzie mogła renderować stronę, musi utworzyć drzewo DOM, analizując znaczniki HTML. Gdy tylko analizator napotka skrypt, musi się zatrzymać i go wykonać, zanim będzie mógł kontynuować analizowanie kodu HTML. Jeśli skrypt dynamicznie wstrzykuje inny skrypt, procesor musi czekać jeszcze dłużej na pobranie zasobu, co może spowodować więcej niż 1 przesyłkę w obie strony sieci i opóźnić czas pierwszego renderowania strony.
W przypadku wolnych połączeń, np. 2G, skrypty zewnętrzne umieszczane dynamicznie za pomocą instrukcji document.write()
mogą opóźniać wyświetlanie zawartości strony głównej o kilkadziesiąt sekund lub spowodować, że strony wcale się nie załadują lub będą ładowały się tak długo, że użytkownik po prostu się podda. Na podstawie pomiarów w Chrome stwierdziliśmy, że strony, które zawierają skrypty innych firm wstawione za pomocą metody document.write()
, ładują się zwykle dwa razy wolniej niż inne strony w 2G.
Dane zostały zebrane w ramach 28-dniowego testu terenowego obejmującego 1% użytkowników stabilnej wersji Chrome, którzy korzystali z połączenia 2G. Zauważyliśmy, że 7,6% wszystkich wczytanych stron w sieci 2G zawierało co najmniej 1 skrypt blokujący parsowanie, który został wstawiony za pomocą document.write()
w dokumencie najwyższego poziomu. Po zablokowaniu wczytywania tych skryptów zaobserwowaliśmy następujące ulepszenia:
- 10% więcej wczytań stron osiągających pierwsze wyrenderowanie treści (wizualne potwierdzenie dla użytkownika, że strona się wczytuje), 25% więcej wczytań stron osiągających stan pełnego zanalizowania i o 10% mniej ponownych wczytań, co sugeruje zmniejszenie frustracji użytkowników.
- 21% spadek średniego czasu (o ponad sekundę krótszy) do momentu pierwszego wyrenderowania treści
- 38% – zmniejszenie średniego czasu analizowania strony, co oznacza poprawę o prawie 6 sekund, czyli znaczne skrócenie czasu wyświetlania treści ważnych dla użytkownika.
Mając to na uwadze, Chrome od wersji 55 interweniuje w imieniu wszystkich użytkowników, gdy wykryje ten znany nieprawidłowy wzorzec, zmieniając sposób obsługi document.write()
w Chrome (patrz Stan Chrome).
W szczególności Chrome nie wykona elementów <script>
wstrzykniętych za pomocą document.write()
, gdy spełnione są wszystkie te warunki:
- Użytkownik ma wolne połączenie, zwłaszcza gdy korzysta z sieci 2G. (w przyszłości zmiana może zostać rozszerzona na innych użytkowników korzystających z wolnych połączeń, np. 3G lub Wi-Fi).
document.write()
znajduje się w dokumencie najwyższego poziomu. Ta interwencja nie dotyczy skryptów document.written w ramkach iframe, ponieważ nie blokują one renderowania strony głównej.- Skrypt w elementach
document.write()
blokuje parser. Skrypty z atrybutamiasync
lubdefer
będą nadal wykonywane. - Skrypt nie jest hostowany w tej samej witrynie. Inaczej mówiąc, Chrome nie będzie interweniować w przypadku skryptów z pasującym eTLD+1 (np. skrypt hostowany na js.example.org wstawiony na www.example.org).
- Skrypt nie znajduje się jeszcze w pamięci podręcznej HTTP przeglądarki. Skrypty w pamięci podręcznej nie będą opóźnione przez sieć i nadal będą się wykonywać.
- Żądanie wyświetlenia strony nie jest ponownym wczytaniem. Chrome nie będzie interweniować, jeśli użytkownik wywoła ponowne wczytanie, i obsługa strony będzie przebiegać normalnie.
Fragmenty kodu innych firm czasami używają document.write()
do wczytywania skryptów.
Na szczęście większość firm zewnętrznych udostępnia alternatywne skrypty wczytywane asynchronicznie, które umożliwiają wczytywanie skryptów innych firm bez blokowania wyświetlania pozostałych treści na stronie.
Jak rozwiązać ten problem?
Najprostszą odpowiedzią jest niewstrzykiwanie skryptów za pomocą document.write()
. Zachowujemy zestaw znanych usług obsługujących ładowarki asynchroniczne, które warto sprawdzać.
Jeśli Twój dostawca nie jest na liście i obsługuje asynchroniczne ładowanie skryptów, poinformuj nas o tym, a my zaktualizujemy stronę, aby pomóc wszystkim użytkownikom.
Jeśli Twój dostawca nie obsługuje asynchronicznego wczytywania skryptów na stronie, skontaktuj się z nim i poinformuj nas i jego, jak to wpłynie na jego usługi.
Jeśli Twój dostawca udostępnia fragment kodu zawierający element document.write()
, możesz dodać do niego atrybut async
lub dodać elementy skryptu za pomocą interfejsu DOM API, np. document.appendChild()
lub parentNode.insertBefore()
.
Jak wykryć, że Twoja witryna jest dotknięta problemem
Istnieje wiele kryteriów, które decydują o tym, czy ograniczenie jest egzekwowane. Jak więc sprawdzić, czy dotyczy ono Twojego konta?
Wykrywanie, kiedy użytkownik korzysta z sieci 2G
Aby zrozumieć potencjalny wpływ tej zmiany, musisz najpierw sprawdzić, ilu użytkowników korzysta z sieci 2G. Możesz wykryć bieżący typ i szybkość sieci użytkownika, korzystając z dostępnego w Chrome interfejsu Network Information API, a potem wysłać powiadomienie do swoich systemów analitycznych lub RUM (Real User Metrics).
if(navigator.connection &&
navigator.connection.type === 'cellular' &&
navigator.connection.downlinkMax <= 0.115) {
// Notify your service to indicate that you might be affected by this restriction.
}
Wykrywanie ostrzeżeń w Chrome DevTools
Od wersji 53 Chrome w Narzędziach deweloperskich wyświetla ostrzeżenia dotyczące problematycznych instrukcji document.write()
. Jeśli document.write()
spełnia kryteria 2–5 (Chrome ignoruje kryteria połączenia podczas wysyłania tego ostrzeżenia), ostrzeżenie będzie wyglądać mniej więcej tak:
Ostrzeżenia w Narzędziach dla programistów Chrome są świetne, ale jak wykrywać takie problemy na dużą skalę? Możesz sprawdzić nagłówki HTTP wysyłane na serwer, gdy nastąpi interwencja.
Sprawdź nagłówki HTTP zasobu skryptu.
Gdy skrypt wstawiony za pomocą document.write
zostanie zablokowany, Chrome wyśle do żądanego zasobu ten nagłówek:
Intervention: <https://shorturl/relevant/spec>;
Gdy zostanie znaleziony skrypt wstawiony za pomocą document.write
i może on zostać zablokowany w innych okolicznościach, Chrome może wysłać:
Intervention: <https://shorturl/relevant/spec>; level="warning"
Nagłówek interwencji zostanie wysłany jako część żądania GET skryptu (asyncjonalnie w przypadku rzeczywistej interwencji).
Co niesie przyszłość?
Początkowo planujemy wykonać tę interwencję, gdy wykryjemy spełnienie kryteriów. Na początku w Chrome 53 wyświetlaliśmy tylko ostrzeżenie w Konsoli dewelopera. (wersja beta została udostępniona w lipcu 2016 r.). Stabilna wersja powinna być dostępna dla wszystkich użytkowników we wrześniu 2016 r.
Zamierzamy zablokować skrypty wstrzyknięte użytkownikom 2G, co nastąpi wstępnie w Chrome 54, czyli stabilnej wersji, która według naszych szacunków zostanie udostępniona wszystkim użytkownikom w połowie października 2016 r. Więcej informacji znajdziesz w artykule o stanie Chrome.
Z czasem będziemy interweniować, gdy któryś z użytkowników będzie miał wolne połączenie (np.wolne 3G lub Wi-Fi). Zapoznaj się z informacjami o stanie Chrome.
Chcesz dowiedzieć się więcej?
Aby dowiedzieć się więcej, zapoznaj się z tymi materiałami:
- Specyfikacja interwencji document.write() i jej pętli sprzężenia zwrotnego
- Stan Chrome (interwencja dla użytkowników korzystających z sieci 2G)
- Stan Chrome (interwencja dla użytkowników z wolnym połączeniem)
- Dokument projektu
- Dodatkowe uzasadnienie tego działania
- blink-dev Intent to implement thread