ES6:Vlastnosti podle testování

TL;DR
Pomocí služby FeatureTests.io můžete provádět testy funkcí funkcí ES6+. Výsledky těchto testů jsou ve výchozím nastavení ukládány do mezipaměti v prohlížeči uživatele a sdíleny na všech webech, které uživatel navštíví a které tuto službu využívají.

V bootstrapperu pro váš web/aplikaci zkontrolujte výsledky těchto testů funkcí, abyste se rozhodli, které soubory je vhodné načíst.

Pokud testy projdou, můžete načíst svůj původní zdroj *.es6.js soubory a víte, že v tomto prohlížeči budou fungovat nativně a výkonně. Pokud některý test selže, vraťte se k načtení již předtranspilovaného kroku sestavování *.es5.js verze vašeho kódu.

Použijte stejnou kontrolní logiku k rozhodnutí, zda prohlížeč uživatele potřebuje velkou knihovnu shim (jako ES6-Shim) nebo zda prohlížeč nepotřebuje žádné (nebo jen několik) polyfillů API.

V podstatě:načtěte pouze kód, který je nezbytný a načtěte jeho nejlepší a nejnativní verzi které prohlížeč podporuje.

Problém

Pokud ve svých aplikacích používáte jakýkoli kód ES6+, je pravděpodobné, že používáte transpiler jako Babel nebo možná Traceur. Tyto nástroje jsou fantastické a docela schopné produkovat transpilované verze vašeho kódu ES6+, které lze spustit v prohlížečích ES5+ (velká většina).

Je tu však jedna nuance, která je do značné míry přehlížena, a smyslem tohoto příspěvku je uvést ji na světlo jako motivaci pro novou službu, kterou jsem spustil, abych pomohl vyřešit problém:FeatureTests.io.

Dovolte mi položit tuto řečnickou otázku/scénář, abych možná ilustroval své obavy:

...

Pokud se nad tímto scénářem na okamžik nebo dva zamyslíte, je pravděpodobné, že na vás vyskočí několik obav. Nejpozoruhodnější je, že si pravděpodobně uvědomujete, že vytvořený transpilovaný kód je větší a možná pomalejší (pokud ne nyní, určitě později, jakmile budou mít prohlížeče šanci optimalizovat implementace nativních funkcí). Vyžaduje také dodání desítek kb polyfill kódu pro opravu prostoru API v prohlížeči.

To vše funguje, ale není to ideální . Nejlepší kód, který můžete doručit do prohlížeče každého uživatele, je ten nejmenší, nejrychlejší a nejlépe přizpůsobený kód, který můžete prakticky poskytnout. Správně!?

Zde je problém:pokud používáte pouze transpilátor s krokem sestavení a bezpodmínečně vždy obsluhujete transpilovaný kód ekvivalentní ES5, nikdy nebudete ve skutečnosti používat žádnou z implementací nativních funkcí. Vždy a navždy budete používat starší, větší a (možná) pomalejší transpilovaný kód.

Prozatím, i když se zdá, že podpora prohlížeče ES6 přetrvává v nižších procentech, nemusí to vypadat jako tak velký problém. Kromě toho, zvážili jste skutečně, kolik z ES6 vaše aplikace/web využívá (nebo brzy využije)?

Můj odhad je, že většina webů bude široce využívat možná 20-30% funkcí ES6. A většina z nich, ne-li všechny, je již implementována v nejnovější verzi téměř každého prohlížeče. Navíc nový prohlížeč Microsoft Edge už má 81% podporu ES6 (v době psaní tohoto článku) a FF/Chrome na ~50-60% rychle doženou.

Nebude to trvat dlouho a významná část vašich uživatelů bude mít plnou podporu ES6 pro každou funkci, kterou váš web/aplikace používá nebo bude v blízké budoucnosti prakticky používat.

Nechcete poskytnout každému uživateli ten nejlepší možný kód?

Řešení

V první řadě pokračujte v transpilaci kódu pomocí svých oblíbených nástrojů. Pokračujte v tom v sestavení.

Když přejdete k nasazení .js soubory do vašeho webového adresáře, který lze načíst do prohlížeče, včetně původních (ES6+) zdrojových souborů i těchto transpilovaných souborů. Také nezapomeňte podle potřeby zahrnout polyfilly. Můžete je například pojmenovat *.es6.js (původní zdroj) a *.es5.js (přeloženo), aby byly rovné. Nebo můžete použít podadresáře es6/ a es5/ je uspořádat. Jsem si jistý, že rozumíte.

Jak se nyní rozhodnete, kdy se váš web/aplikace načte poprvé, která sada souborů je vhodná k načtení pro prohlížeč jednotlivých uživatelů?

Potřebujete bootstrapper, který se načte jako první, hned zepředu. Například odešlete stránku HTML s jediným <script> a obsahuje buď vložený kód, nebo odkaz na jeden .js soubor. Mnoho webů/aplikací jakékoli složitosti to již v té či oné podobě dělá. Je docela typické načíst malý bootstrapper, který pak nastaví a nahraje zbytek vaší aplikace.

Pokud ještě nemáte takovou techniku, není to vůbec těžké a získáte mnoho výhod, včetně možnosti podmíněně načíst příslušné verze souborů pro každý prohlížeč, jak vysvětlím v moment. Ve skutečnosti to není tak zastrašující, jak se může zdát.

Mimochodem:způsob, jakým to osobně dělám, je vložit kód zavaděče LABjs (jen ~2,2k minzip) a pak ve stejném souboru provést $LAB.script(..).. řetěz(y) pro načtení zbytku mých souborů. Tento soubor nazývám "load.js" a načtu to jedním <script src=..></script> tag v mém původním HTML. Všechny ostatní JS se dynamicky načítají paralelně co nejvýkonněji.

Jak se nyní ve vašem bootstrapperu (jakkoli máte nastavený) rozhodnete, které soubory načíst?

Musíte otestovat funkce této instance prohlížeče rozhodnout, jaké jsou její možnosti. Pokud jsou podporovány všechny funkce, které potřebujete, načtěte *.es6.js soubory. Pokud některé chybí, načtěte polyfilly a *.es5.js soubory.

A je to. Opravdu. Ne, opravdu, to je vše, co navrhuji.

Testování funkcí ES6

Testování funkcí pro API je snadné. Jsem si jistý, že pravděpodobně víte, jak dělat věci jako:

if (Number.isNaN) {
    numberIsNaN = true;
}
else {
    numberIsNaN = false;
}

Ale co syntaxe, jako je zjišťování, zda prohlížeč podporuje => funkce šipky nebo let deklarace blokového rozsahu?

To je těžší, protože to nefunguje tak, jak bychom mohli doufat:

try {
    x = y => y;
    arrows = true;
}
catch (err) {
    arrows = false;
}

Syntaxe selže při kompilaci JS (v prohlížečích kompatibilních s předchozí verzí ES6), než se vůbec pokusí spustit, takže try..catch nemůže to chytit. Řešení? Odložit kompilaci.

try {
    new Function( "(y => y)" );
    arrows = true;
}
catch (err) {
    arrows = false;
}

new Function(..) konstruktor zkompiluje kód zadaný za běhu, takže jakákoli chyba kompilace může být zachycena vaším try..catch .

Skvělé, problém vyřešen.

Ale chcete osobně navrhnout testy funkcí pro všechny různé funkce ES6+, které plánujete používat? A některé z nich by mohly být mírně bolestivé (pomalé) při běhu (jako u TCO), takže je opravdu chcete dělat? Nebylo by lepší spouštět testy ve vláknu Web Worker na pozadí, aby se minimalizoval jakýkoli dopad na výkon hlavního vlákna uživatelského rozhraní?

A i když jste se do všech těch potíží dostali, opravdu Potřebujete provést všechny tyto testy pokaždé, když se načte jedna z vašich stránek? Prohlížeče nepřidávají nové funkce každou minutu. Prohlížeč uživatele se může aktualizovat v nejlepším případě každých pár týdnů, možná měsíců. Nemohli jste jednou spustit testy a výsledky na chvíli uložit do mezipaměti?

Pokud jsou však tyto výsledky uložené v mezipaměti dostupné pouze pro váš web, pokud váš uživatel navštíví jiné weby řízené ES6, každý z nich bude muset znovu provést svou vlastní sadu testů. Nebylo by hezčí, kdyby výsledky testu mohly být ukládány do mezipaměti „globálně“ v prohlížeči daného uživatele, takže jakýkoli web by mohl používat pouze true / false výsledky testů, aniž byste museli znovu spouštět všechny testy?

Nebo to otočím:nebylo by hezké, kdyby se váš uživatel objevil na vašem webu a výsledky již byly uloženy do mezipaměti (návštěvou jiného webu), takže nemusel čekat, až je váš web spustí? a váš web se jim tak načítal rychleji?

FeatureTests.io

Všechny tyto důvody (a další) jsou důvodem, proč jsem vytvořil ES Feature Tests jako službu :FeatureTests.io.

Tato služba poskytuje soubor knihovny https://featuretests.io/rs.js, který za vás provede veškerou práci, kterou jsem uvedl výše. Tento soubor knihovny si vyžádáte buď dříve nebo jako váš bootstrapper se načte a poté jednoduše zkontrolujete výsledky testů (které se načítají z mezipaměti nebo se spouštějí automaticky) pomocí jednoduchého if prohlášení.

Chcete-li například otestovat, zda je vaše let a => pomocí souborů lze načíst, toto byste udělali ve svém bootstrapperu:

window["Reflect.supports"]( "all", function(results){
    if (results.letConst && results.arrow) {
        // load `*.es6.js` files
    }
    else {
        // load already pre-transpiled `*.es5.js` files
    }
} );

Pokud váš web ještě neuložil výsledky pro tohoto uživatele do mezipaměti, knihovna komunikuje mezi doménami (prostřednictvím <iframe> z vašeho webu na featuretests.io ), takže výsledky testu mohou být uloženy nebo načteny „globálně“ v tomto prohlížeči.

Pokud je potřeba spustit testy, spustí Web Worker, který provede testy mimo vlákno. Dokonce se pokouší použít sdíleného webového pracovníka, takže pokud uživatel současně načítá 2 a více webů, které službu využívají, oba používají stejnou instanci pracovníka.

Veškerou tuto logiku získáte automaticky pomocí tohoto zdarma službu.

A je to! To je vše, co potřebujete, abyste mohli začít s podmíněným rozděleným načítáním kódu vašeho webu/aplikace na základě testů funkcí ES6 v prohlížeči.

Pokročilé položky

Knihovna za tímto webem je open-source:es-feature-tests. Je také k dispozici na npm.

Pokud byste chtěli, mohli byste vložit testy z knihovny do svého vlastního kódu bootstrapperu a přeskočit pomocí FeatureTests.io. Ztratíte tím výhody sdíleného ukládání do mezipaměti a vše ostatní, ale stále to znamená, že nemusíte zjišťovat své vlastní testy.

Nebo služba nabízí koncový bod API, který vrací testy v textové podobě, takže je můžete načíst na svém serveru během kroku sestavování a poté zahrnout a provést tyto testy ve svém vlastním kódu.

Balíček npm je samozřejmě kompatibilní s Node/iojs, takže můžete dokonce spustit úplně stejný druh testování funkcí pro rozdělené načítání uvnitř vašich programů Node, jako:

var ReflectSupports = require("es-feature-tests");

ReflectSupports( "all", function(results){
    if (results.letConst && results.arrow) {
        // require(..) `*.es6.js` modules
    }
    else {
        // require(..) already pre-transpiled
        // `*.es5.js` modules
    }
} );

Jaké výsledky testu můj kód potřebuje?

Jak jsem uvedl dříve, pravděpodobně nebudete muset kontrolovat každý jednotlivý výsledek testu, protože pravděpodobně nevyužijete 100 % všech funkcí ES6+.

Ale neustále sledujte, které výsledky testů vaše if příkaz by měl zkontrolovat může být zdlouhavý a náchylný k chybám. Pamatujete si, jestli někdo někdy použil let ve vašem kódu nebo ne?

Balíček "es-feature-tests" obsahuje nástroj CLI nazvaný testify který dokáže skenovat soubory nebo adresáře vašeho kódu vytvořeného ES6 a automaticky pro vás vytvoří ekvivalentní kontrolní logiku. Například:

$> bin/testify --dir=/path/to/es6-code/

function checkFeatureTests(testResults){return testResults.letConst&&testResults.arrow}

Upozornění: V době psaní tohoto článku toto testify nástroj je extrémně hackerský a WiP. Nakonec provede úplnou a kompletní analýzu, ale zatím je to opravdu drsné. Zůstaňte naladěni na další aktualizace tohoto nástroje již brzy!

Můžete použít testify ve vašem procesu sestavování (pravděpodobně před transpilací), abyste naskenovali zdrojové soubory ES6 a vytvořili checkFeatureTests(..) deklarace funkce, která kontroluje všechny výsledky testů, které váš kód potřebuje.

Nyní tento kód vložíte do svého bootstrapperu, takže nyní zní:

// ..

function checkFeatureTests(testResults){return testResults.letConst&&testResults.arrow}

window["Reflect.supports"]( "all", function(results){
    if (checkFeatureTests(results)) {
        // load `*.es6.js` files
    }
    else {
        // load already pre-transpiled `*.es5.js` files
    }
} );

// ..

Tento nástroj CLI se sestavením zajistí, že vaše testy budou vždy automaticky vyladěny na kód, který jste napsali, což vám umožní nastavit jej a zapomenout pokud jde o to, aby byl kód vašeho webu/aplikace vždy načten v nejlepší možné verzi pro každý prohlížeč.

Přehled

Chci, abyste napsali kód ES6, a chci, abyste to začali dělat dnes. Napsal jsem knihu o ES6, která vám pomůže se to naučit:You Don't Know JS:ES6 &Beyond , kterou si můžete buď přečíst zdarma online, nebo si ji zakoupit v O'Reilly či jiných knihkupectvích.

Chci však, abyste byli zodpovědní a optimálně zasílali kód ES6 nebo transpilovaný kód do prohlížečů vašich uživatelů. Chci, abychom všichni těžili z úžasné práce, kterou odvádějí prohlížeče na nativní implementaci těchto funkcí.

Načtěte nejlepší kód pro každý prohlížeč – nic více, nic méně. Doufáme, že vám FeatureTests.io pomůže s tímto cílem.

Happy ES6'ing!