třes stromů
třes stromů je termín běžně používaný v kontextu JavaScriptu pro eliminaci mrtvého kódu. Spoléhá se na statickou strukturu syntaxe modulu ES2015, tj. import
a export
. Název a koncept byly popularizovány kumulativním balíčkem modulů ES2015.
vydání webpack 2 přišlo s vestavěnou podporou modulů ES2015 (alias harmony modules) a také s nepoužívanou detekcí exportu modulů. Nový webpack 4 vydání rozšiřuje tuto schopnost s způsob, jak poskytovat rady kompilátor pomocí "sideEffects"
package.json
majetek k označení, které soubory v projektu jsou „čisté“, a proto bezpečné prořezávat, pokud se nepoužívá.
Přidat Nástroj
Pojďme přidat nové utility soubor do našeho projektu, src/math.js
, který exportuje dvě funkce:
projekt
webpack-demo|- package.json|- webpack.config.js|- /dist |- bundle.js |- index.html|- /src |- index.js+ |- math.js|- /node_modules
src/math.js
export function square(x) { return x * x;}export function cube(x) { return x * x * x;}
Nastavte mode
možnost konfigurace na rozvoj ujistěte se, že svazek není minified:
webpack.konfigurace.js
const path = require('path');module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), },+ mode: 'development',+ optimization: {+ usedExports: true,+ },};
S tím, že v místě, pojďme aktualizovat náš vstup skriptu využít jeden z těchto nových metod a odstranit lodash
pro jednoduchost:
src/index.js
- import _ from 'lodash';+ import { cube } from './math.js'; function component() {- const element = document.createElement('div');+ const element = document.createElement('pre');- // Lodash, now imported by this script- element.innerHTML = _.join(, ' ');+ element.innerHTML = .join('\n\n'); return element; } document.body.appendChild(component());
Všimněte si, že jsme neměli import
square
metoda src/math.js
modul. Tato funkce je známá jako „mrtvý kód“, což znamená nepoužitý export
, který by měl být zrušen. Nyní spusťte náš skript npm, npm run build
a zkontrolujte výstupní svazek:
Dist / bundle.js (around lines 90 – 100)
/* 1 *//***/ (function (module, __webpack_exports__, __webpack_require__) { 'use strict'; /* unused harmony export square */ /* harmony export (immutable) */ __webpack_exports__ = cube; function square(x) { return x * x; } function cube(x) { return x * x * x; }});
poznamenejte si unused harmony export square
komentář výše. Pokud se podíváte na kód pod ním, zjistíte, že square
není importován, je však stále součástí balíčku. Opravíme to v další části.
označte soubor jako bez vedlejších účinků
ve světě 100% ESM modulů je identifikace vedlejších účinků přímočará. Nicméně, ještě tam nejsme, takže mezitím je nutné poskytnout rady překladači webpacku o „čistotě“ vašeho kódu.
způsob, jak toho dosáhnout ,je"sideEffects"
balíček.json property.
{ "name": "your-project", "sideEffects": false}
kód výše uvedeno, neobsahuje vedlejší účinky, takže můžeme jednoduše označit majetek jako false
informovat webpack, které je možné bezpečně prořezávat nepoužité vývozu.
Pokud má váš kód nějaké vedlejší účinky, může být místo toho poskytnuto pole:
{ "name": "your-project", "sideEffects": }
pole přijímá jednoduché vzory glob do příslušných souborů. Používá glob–regexp pod kapotou (Podporuje: *
**
{a,b}
). Vzory jako
*.css
, které neobsahují /
, bude zacházeno jako **/*.css
.
{ "name": "your-project", "sideEffects": }
konečně, "sideEffects"
lze také nastavit z možnosti konfigurace module.rules
.
Vyjasnění strom třese a vedlejší účinky
sideEffects
usedExports
(více známý jako strom třese) optimalizace jsou dvě různé věci.
sideEffects
je mnohem efektivnější, protože umožňuje přeskočit celé moduly/soubory a celý podstrom.
usedExports
spoléhá na terser k detekci vedlejších účinků ve výkazech. Je to obtížný úkol v JavaScriptu a není tak účinný jako přímočarýsideEffects
flag. To také nemůže přeskočit subtree / závislosti, protože spec říká, že vedlejší účinky je třeba vyhodnotit. Zatímco funkce exportu funguje dobře, komponenty vyššího řádu React (HOC) jsou v tomto ohledu problematické.
příklad:
import { Button } from '@shopify/polaris';
pre-svázaný verze vypadá takhle:
import hoistStatics from 'hoist-non-react-statics';function Button(_ref) { // ...}function merge() { var _final = {}; for ( var _len = arguments.length, objs = new Array(_len), _key = 0; _key < _len; _key++ ) { objs = arguments; } for (var _i = 0, _objs = objs; _i < _objs.length; _i++) { var obj = _objs; mergeRecursively(_final, obj); } return _final;}function withAppProvider() { return function addProvider(WrappedComponent) { var WithProvider = /*#__PURE__*/ (function (_React$Component) { // ... return WithProvider; })(Component); WithProvider.contextTypes = WrappedComponent.contextTypes ? merge(WrappedComponent.contextTypes, polarisAppProviderContextTypes) : polarisAppProviderContextTypes; var FinalComponent = hoistStatics(WithProvider, WrappedComponent); return FinalComponent; };}var Button$1 = withAppProvider()(Button);export { // ..., Button$1,};
Když Button
je nepoužitý můžete účinně odstranit export { Button$1 };
což ponechává všechny zbývající kód. Otázka tedy zní: „má tento kód nějaké vedlejší účinky nebo může být bezpečně odstraněn?“. Těžko říci, zejména kvůli tomuto řádku withAppProvider()(Button)
withAppProvider
je volán a návratová hodnota je také volána. Existují nějaké vedlejší účinky při volání merge
nebo hoistStatics
? Existují vedlejší účinky při přiřazování WithProvider.contextTypes
(setr?) nebo při čtení WrappedComponent.contextTypes
(Getter?).
Terser se to vlastně snaží zjistit, ale v mnoha případech to neví jistě. To neznamená, že terser nedělá svou práci dobře, protože na to nemůže přijít. Je příliš obtížné ji spolehlivě určit v dynamickém jazyce, jako je JavaScript.
ale můžeme pomoci terser pomocí/*#__PURE__*/
anotace. Označuje prohlášení jako vedlejší efekt zdarma. Jednoduchá změna by tedy umožnila setřást kód stromem:
var Button$1 = /*#__PURE__*/ withAppProvider()(Button);
to by umožnilo odstranit tento kus kódu. Stále však existují otázky týkající se dovozu, které je třeba zahrnout / vyhodnotit, protože by mohly obsahovat vedlejší účinky.
k řešení tohoto problému použijeme vlastnost "sideEffects"
v package.json
.
je to podobné /*#__PURE__*/
ale na úrovni modulu místo úrovně příkazu. To říká, že ("sideEffects"
vlastnost): „Pokud žádný přímý export z modulu označeny žádné vedlejší účinky se používá, bundler může přeskočit hodnocení modul pro nežádoucí účinky.“.
v příkladu Shopify Polaris vypadají původní moduly takto:
index.js
import './configure';export * from './types';export * from './components';
komponenty / index.js
// ...export { default as Breadcrumbs } from './Breadcrumbs';export { default as Button, buttonFrom, buttonsFrom } from './Button';export { default as ButtonGroup } from './ButtonGroup';// ...
package.json
// ..."sideEffects": ,// ...
Na import { Button } from "@shopify/polaris";
má to následující důsledky:
- zahrnují: obsahovat modul, vyhodnotit a pokračovat v analýze závislostí
- přeskočit: ne, patří to, nechci hodnotit, ale pokračovat v analýze závislostí
- vyloučit to: nechci, zahrnout, nechci hodnotit a analyzovat závislosti
Konkrétně za odpovídající zdroj(s):
-
index.js
: Žádný přímý export se používá, ale označeny účinky -> zahrnout -
configure.js
: Ne export se používá, ale označeny účinky -> zahrnout -
types/index.js
: Ne export se používá, nejsou označeny účinky -> vyloučit -
components/index.js
: Bez přímého vývozu se používá, není označena s účinky, ale reexported vývoz se používají -> přeskočit -
components/Breadcrumbs.js
: Nepoužívá se žádný export, není označen sideEffects – > vyloučit. To také vyloučeny všechny závislostí, jako jecomponents/Breadcrumbs.css
i když jsou označeny účinky. -
components/Button.js
: Přímý vývoz se používá, nejsou označeny účinky -> zahrnout -
components/Button.css
: Ne export se používá, ale označeny účinky -> zahrnout
V tomto případě pouze 4 moduly jsou zahrnuty do svazku:
-
index.js
: skoro prázdný configure.js
components/Button.js
components/Button.css
Po této optimalizaci, další optimalizace může ještě použít. Například: buttonFrom
a buttonsFrom
exporty z Button.js
jsou také nepoužité. usedExports
optimalizace ji vyzvedne a terser může být schopen vypustit některé příkazy z modulu.
platí také zřetězení modulu. Takže tyto 4 moduly plus vstupní modul (a pravděpodobně více závislostí) mohou být zřetězeny. index.js
nemá na konci vygenerovaný žádný kód.
Označit volání funkce jako vedlejší účinek zdarma
je možné říct, webpack, že volání funkce není strana účinek zdarma (pure) pomocí /*#__PURE__*/
anotace. Lze jej umístit před volání funkcí a označit je jako bez vedlejších účinků. Argumenty předané funkci nejsou označeny anotací a může být nutné je označit jednotlivě. Když je počáteční hodnota v proměnné prohlášení o nepoužité proměnné je považován za vedlejší účinek zdarma (pure), je stále označen jako mrtvý kód, nebude vykonán, a klesl o minimizer. Toto chování je povoleno, pokud je optimization.innerGraph
nastaveno na true
.
soubor.js
/*#__PURE__*/ double(55);
Minify Výstup
Takže máme přichystané naše „mrtvý kód“, aby se snížil pomocí import
export
syntaxe, ale stále je třeba, aby ji pokles ze svazku. K tomu, nastavte mode
možnost konfigurace production
.
webpack.konfigurace.js
const path = require('path');module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist'), },- mode: 'development',- optimization: {- usedExports: true,- }+ mode: 'production',};
S tou druhou stranou, můžeme spustit další npm run build
a uvidíme, jestli se něco změnilo.
Všimněte si něco jiného o dist/bundle.js
? Jasně celý svazek je teď minified a rozbité, ale když se podíváte pozorně, neuvidíte square
funkce zahrnuty, ale bude vidět zmrzačené verze cube
funkce (function r(e){return e*e*e}n.a=r
). Díky minifikaci a třesu stromů je náš balíček nyní o několik bajtů menší! I když se to v tomto vymyšleném příkladu nemusí zdát jako moc, třepání stromů může při práci na větších aplikacích s komplexními stromy závislostí přinést významné snížení velikosti svazku.
závěr
takže jsme se naučili, že abyste mohli využít třesu stromů, musíte…
- použijte syntaxi modulu ES2015 (tj.
import
aexport
). - Zajistit, žádné kompilátory transformovat své ES2015 modul syntaxe do CommonJS modules (toto je výchozí chování populární Babel přednastavených @babel/preset-env – naleznete v dokumentaci pro další podrobnosti).
- přidejte vlastnost
"sideEffects"
do souborupackage.json
. - Použití
production
mode
konfigurace možnost povolit různé optimalizace včetně minification a strom třese.
svou aplikaci si můžete představit jako strom. Zdrojový kód a knihovny, které skutečně používáte, představují zelené, živé listy stromu. Mrtvý kód představuje hnědé, mrtvé listy stromu, které jsou spotřebovány na podzim. Chcete-li se zbavit mrtvých listů, musíte strom potřást a způsobit, že padnou.
Máte-li zájem o více způsobů, jak optimalizovat svůj výstup, přejděte na další průvodce pro podrobnosti o stavbě pro výrobu.