Er komt invoer naar de Compositor
Dit is de laatste van de vierdelige blogreeks die een kijkje in Chrome neemt; onderzoeken hoe het omgaat met onze code om een website weer te geven. In het vorige bericht hebben we naar het weergaveproces gekeken en meer geleerd over de compositor . In dit bericht bekijken we hoe de compositor een soepele interactie mogelijk maakt wanneer gebruikersinvoer binnenkomt.
Voer gebeurtenissen in vanuit het perspectief van de browser
Wanneer u 'invoergebeurtenissen' hoort, denkt u misschien alleen aan het typen in een tekstvak of een muisklik, maar vanuit het perspectief van de browser betekent invoer elk gebaar van de gebruiker. Scrollen met het muiswiel is een invoergebeurtenis en aanraken of er overheen bewegen is ook een invoergebeurtenis.
Wanneer een gebruikersgebaar zoals aanraking op een scherm plaatsvindt, is het browserproces het proces dat het gebaar als eerste ontvangt. Het browserproces is zich echter alleen bewust van waar dat gebaar plaatsvond, aangezien de inhoud binnen een tabblad wordt afgehandeld door het rendererproces. Het browserproces verzendt dus het gebeurtenistype (zoals touchstart
) en de coördinaten ervan naar het rendererproces. Het Renderer-proces handelt de gebeurtenis op de juiste manier af door het gebeurtenisdoel te vinden en de gekoppelde gebeurtenislisteners uit te voeren.
Compositor ontvangt invoergebeurtenissen
In het vorige bericht hebben we gekeken hoe de compositor soepel met scrollen om kon gaan door gerasterde lagen samen te stellen. Als er geen invoergebeurtenislisteners aan de pagina zijn gekoppeld, kan de Compositor-thread een nieuw samengesteld frame maken, volledig onafhankelijk van de hoofdthread. Maar wat als er enkele gebeurtenislisteners aan de pagina waren gekoppeld? Hoe komt de compositor-thread erachter of de gebeurtenis moet worden afgehandeld?
Niet-snel scrollbare regio begrijpen
Omdat het uitvoeren van JavaScript de taak van de hoofdthread is, markeert de compositor-thread, wanneer een pagina wordt samengesteld, een gebied van de pagina waaraan gebeurtenishandlers zijn gekoppeld als "Niet-snel scrollbare regio". Door over deze informatie te beschikken, kan de compositor-thread ervoor zorgen dat de invoergebeurtenis naar de hoofdthread wordt verzonden als de gebeurtenis in die regio plaatsvindt. Als de invoergebeurtenis van buiten dit gebied komt, gaat de compositor-thread door met het samenstellen van een nieuw frame zonder op de hoofdthread te wachten.
Houd er rekening mee wanneer u gebeurtenishandlers schrijft
Een veelgebruikt patroon voor het afhandelen van gebeurtenissen bij webontwikkeling is het delegeren van gebeurtenissen. Omdat gebeurtenissen bubbelen, kunt u één gebeurtenishandler aan het bovenste element koppelen en taken delegeren op basis van het gebeurtenisdoel. Mogelijk heb je code zoals hieronder gezien of geschreven.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
Omdat u slechts één gebeurtenishandler voor alle elementen hoeft te schrijven, is de ergonomie van dit gebeurtenisdelegatiepatroon aantrekkelijk. Als je deze code echter vanuit het perspectief van de browser bekijkt, is de hele pagina nu gemarkeerd als een niet-snel scrollbaar gebied. Dit betekent dat zelfs als uw applicatie niets geeft om invoer van bepaalde delen van de pagina, de thread van de compositor moet communiceren met de hoofdthread en daarop moet wachten elke keer dat er een invoergebeurtenis binnenkomt. Dus het soepele scrollvermogen van de compositor wordt verslagen.
Om te voorkomen dat dit gebeurt, kunt u passive: true
opties doorgeven in uw gebeurtenislistener. Dit geeft aan de browser aan dat je nog steeds naar de gebeurtenis in de hoofdthread wilt luisteren, maar de compositor kan doorgaan en ook een nieuw frame samenstellen.
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
Controleer of het evenement kan worden geannuleerd
Stel je voor dat je een vak op een pagina hebt waarin je de scrollrichting wilt beperken tot alleen horizontaal scrollen.
Het gebruik van passive: true
optie in uw pointer-gebeurtenis betekent dat het scrollen van de pagina soepel kan verlopen, maar het verticale scrollen kan al zijn begonnen tegen de tijd dat u Default wilt preventDefault
om de scrollrichting te beperken. U kunt dit controleren door event.cancelable
-methode te gebruiken.
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
event.preventDefault(); // block the native scroll
/*
* do what you want the application to do here
*/
}
}, {passive: true});
Als alternatief kunt u CSS-regels zoals touch-action
gebruiken om de gebeurtenishandler volledig te elimineren.
#area {
touch-action: pan-x;
}
Het evenementdoel vinden
Wanneer de compositorthread een invoergebeurtenis naar de hoofdthread verzendt, is het eerste dat moet worden uitgevoerd een hittest om het gebeurtenisdoel te vinden. Hittest maakt gebruik van verfregistratiegegevens die zijn gegenereerd tijdens het weergaveproces om erachter te komen wat zich onder de puntcoördinaten bevindt waarin de gebeurtenis plaatsvond.
Minimaliseren van gebeurtenisverzendingen naar de hoofdthread
In het vorige bericht hebben we besproken hoe ons typische beeldscherm het scherm 60 keer per seconde vernieuwt en hoe we de cadans moeten bijhouden voor vloeiende animaties. Voor invoer levert een typisch touchscreen-apparaat aanrakingsgebeurtenissen 60-120 keer per seconde, en een typische muis levert gebeurtenissen 100 keer per seconde. Invoergebeurtenis heeft een hogere betrouwbaarheid dan ons scherm kan vernieuwen.
Als een continue gebeurtenis zoals touchmove
120 keer per seconde naar de hoofdthread wordt verzonden, kan dit een buitensporig aantal hittests en JavaScript-uitvoering veroorzaken in vergelijking met hoe langzaam het scherm kan worden vernieuwd.
Om overmatige aanroepen naar de hoofdthread te minimaliseren, voegt Chrome continue gebeurtenissen samen (zoals wheel
, mousewheel
, mousemove
, pointermove
, touchmove
) en vertraagt de verzending tot vlak voor de volgende requestAnimationFrame
.
Alle discrete gebeurtenissen zoals keydown
, keyup
, mouseup
, mousedown
, touchstart
en touchend
worden onmiddellijk verzonden.
Gebruik getCoalescedEvents
om intra-frame-gebeurtenissen op te halen
Voor de meeste webapplicaties zouden samengevoegde gebeurtenissen voldoende moeten zijn om een goede gebruikerservaring te bieden. Als u echter zaken bouwt zoals het tekenen van een toepassing en het plaatsen van een pad op basis van touchmove
, kunt u tussenliggende coördinaten kwijtraken om een vloeiende lijn te tekenen. In dat geval kunt u de methode getCoalescedEvents
in de pointer-gebeurtenis gebruiken om informatie over die samengevoegde gebeurtenissen op te halen.
window.addEventListener('pointermove', event => {
const events = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// draw a line using x and y coordinates.
}
});
Volgende stappen
In deze serie hebben we de innerlijke werking van een webbrowser besproken. Als je er nog nooit over hebt nagedacht waarom DevTools aanbeveelt om {passive: true}
toe te voegen aan je gebeurtenishandler of waarom je async
attribuut in je scripttag zou kunnen schrijven, hoop ik dat deze serie enig licht werpt op waarom een browser deze informatie nodig heeft om sneller en soepeler te kunnen leveren webervaring.
Gebruik vuurtoren
Als u ervoor wilt zorgen dat uw code vriendelijk is voor de browser, maar geen idee heeft waar u moet beginnen, is Lighthouse een tool die een audit van elke website uitvoert en u een rapport geeft over wat er goed wordt gedaan en wat er moet worden verbeterd. Als u de lijst met audits doorneemt, krijgt u ook een idee van waar een browser om geeft.
Leer hoe u prestaties kunt meten
Prestatieaanpassingen kunnen per site verschillen. Het is dus van cruciaal belang dat u de prestaties van uw site meet en beslist wat het beste bij uw site past. Het Chrome DevTools-team heeft enkele tutorials over hoe u de prestaties van uw site kunt meten .
Voeg functiebeleid toe aan uw site
Als u een extra stap wilt zetten, is Feature Policy een nieuwe webplatformfunctie die u als vangrail kan dienen bij het bouwen van uw project. Het inschakelen van featurebeleid garandeert het bepaalde gedrag van uw app en voorkomt dat u fouten maakt. Als u er bijvoorbeeld zeker van wilt zijn dat uw app het parseren nooit blokkeert, kunt u uw app uitvoeren op basis van het beleid voor synchrone scripts. Wanneer sync-script: 'none'
is ingeschakeld, wordt voorkomen dat parser-blokkerende JavaScript wordt uitgevoerd. Dit voorkomt dat uw code de parser blokkeert en dat de browser zich geen zorgen hoeft te maken over het pauzeren van de parser.
Sluit af
Toen ik begon met het bouwen van websites, maakte het me bijna alleen maar uit hoe ik mijn code zou schrijven en wat me zou helpen productiever te zijn. Die dingen zijn belangrijk, maar we moeten ook nadenken over hoe de browser de code verwerkt die we schrijven. Moderne browsers investeren en blijven investeren in manieren om gebruikers een betere webervaring te bieden. Door aardig te zijn voor de browser door onze code te organiseren, verbetert u op zijn beurt uw gebruikerservaring. Ik hoop dat je met mij meegaat in de zoektocht om aardig te zijn tegen de browsers!
Hartelijk dank aan iedereen die vroege versies van deze serie heeft beoordeeld, inclusief (maar niet beperkt tot): Alex Russell , Paul Irish , Meggin Kearney , Eric Bidelman , Mathias Bynens , Addy Osmani , Kinuko Yasuda , Nasko Oskov en Charlie Reis.
Vond je deze serie leuk? Als je vragen of suggesties hebt voor het toekomstige bericht, hoor ik graag van je in het commentaargedeelte hieronder of via @kosamari op Twitter.