Articles

fa rázása

a fa rázása a JavaScript kontextusában általánosan használt kifejezés a holtkód megszüntetésére. Az ES2015 modul szintaxisának statikus struktúrájára támaszkodik, azaz import és export. A nevet és a koncepciót az ES2015 modul bundler rollup népszerűsítette.

a webpack 2 kiadás beépített támogatást kapott az ES2015 modulokhoz (alias harmony modules), valamint a fel nem használt modul export detektálásához. Az új webpack 4 kiadás kibővíti ezt a képességet azzal a módszerrel, hogy tippeket adjon a fordítónak a "sideEffects"package.json tulajdonságon keresztül, jelezve, hogy a projekt mely fájljai “tisztaak”, ezért használaton kívül biztonságosan metszhetők.

adjon hozzá egy segédprogramot

adjunk hozzá egy új segédprogram fájlt a projektünkhöz,src/math.js, amely két funkciót exportál:

project

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;}

állítsa be a mode konfigurációs opciót fejlesztésre, hogy megbizonyosodjon arról, hogy a csomag nincs minifikálva:

webpack.config.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,+ },};

ennek a helyén frissítsük a belépési szkriptünket az új módszerek egyikének használatához, és távolítsuk el alodash az egyszerűség kedvéért:

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());

vegye figyelembe, hogy mi nem importa squaremódszer a src/math.js modul. Ez a funkció az úgynevezett “halott kód”, ami egy fel nem használt export, amelyet el kell dobni. Most futtassuk le az npm szkriptünket, npm run build, és vizsgáljuk meg a kimeneti csomagot:

dist/bundle.js (a 90 – 100 vonalak körül)

/* 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; }});

vegye figyelembe a unused harmony export square fenti megjegyzést. Ha megnézi az alatta lévő kódot, észreveszi, hogy a square nem importálódik, azonban továbbra is szerepel a csomagban. Ezt a következő részben kijavítjuk.

jelölje meg a fájlt mellékhatásmentesként

egy 100%-os ESM modul világban a mellékhatások azonosítása egyszerű. Azonban még nem vagyunk ott, ezért időközben tippeket kell adni a webpack fordítójának a kód “tisztaságáról”.

ennek módja a "sideEffects" csomag.JSON tulajdon.

{ "name": "your-project", "sideEffects": false}

a fent említett összes kód nem tartalmaz mellékhatásokat, ezért egyszerűen megjelölhetjük a tulajdonságot false hogy tájékoztassuk a webpack-et arról, hogy biztonságosan meg tudja metszeni a fel nem használt exportokat.

Ha a kódnak volt néhány mellékhatása, akkor ehelyett egy tömb adható meg:

{ "name": "your-project", "sideEffects": }

a tömb elfogadja az egyszerű glob mintákat a megfelelő fájlokhoz. Ez használ glob-to-regexp a motorháztető alatt (támogatja: ***{a,b}). Az olyan mintákat, mint a *.css, amelyek nem tartalmazzák a /, úgy kezeljük, mint **/*.css.

{ "name": "your-project", "sideEffects": }

végül a "sideEffects"a module.rules konfigurációs opcióból is beállítható.

A fa rázásának és oldalhatásainak tisztázása

asideEffects ésusedExports (ismertebb nevén fa rázása) optimalizálás két különböző dolog.

sideEffects sokkal hatékonyabb, mivel lehetővé teszi a teljes modulok/fájlok és a teljes részfa kihagyását.

usedExports terserre támaszkodik a mellékhatások kimutatásában. Ez egy nehéz feladat a JavaScript-ben, és nem olyan hatékony, mint az egyszerű sideEffects zászló. Nem hagyhatja ki a részfát/függőségeket sem, mivel a specifikáció szerint a mellékhatásokat értékelni kell. Míg a függvény exportálása jól működik, a React magasabb rendű komponensei (HOC) problematikusak ebben a tekintetben.

tegyünk egy példát:

import { Button } from '@shopify/polaris';

az előre csomagolt verzió így néz ki:

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,};

Ha a Button nem használt, akkor hatékonyan eltávolíthatja a export { Button$1 }; amely meghagyja az összes fennmaradó kódot. Tehát a kérdés az, hogy “van-e ennek a kódnak bármilyen mellékhatása, vagy biztonságosan eltávolítható?”. Nehéz megmondani, különösen ezen sor miatt withAppProvider()(Button)withAppProvider meghívásra kerül, és a visszatérési érték is meghívásra kerül. Vannak-e mellékhatások a merge vagy hoistStaticshívásakor? Vannak-e mellékhatások a WithProvider.contextTypes (szetter?) vagy WrappedComponent.contextTypes (Getter?).

Terser valójában megpróbálja kitalálni, de sok esetben nem tudja biztosan. Ez nem azt jelenti, hogy a terser nem jól végzi a munkáját, mert nem tudja kitalálni. Túl nehéz megbízhatóan meghatározni egy olyan dinamikus nyelven, mint a JavaScript.

de segíthetünk tersernek a /*#__PURE__*/ annotáció használatával. Ez zászlók nyilatkozatot mellékhatásként ingyenes. Tehát egy egyszerű változtatás lehetővé tenné a kód fa rázását:

var Button$1 = /*#__PURE__*/ withAppProvider()(Button);

Ez lehetővé tenné a kóddarab eltávolítását. De még mindig vannak olyan kérdések az importokkal kapcsolatban, amelyeket be kell vonni/értékelni kell, mert mellékhatásokat tartalmazhatnak.

ennek kezelésére a"sideEffects"tulajdonságot használjukpackage.json.

hasonló a /*#__PURE__*/ – hez, de modul szinten, nem pedig utasítás szinten. Azt mondja ("sideEffects" tulajdonság): “ha nem használunk közvetlen exportot egy olyan modulból, amely nem tartalmaz mellékhatást, akkor a bundler kihagyhatja a modul értékelését a mellékhatások szempontjából.”.

a Shopify Polaris példájában az eredeti modulok így néznek ki:

index.js

import './configure';export * from './types';export * from './components';

összetevők/index.js

// ...export { default as Breadcrumbs } from './Breadcrumbs';export { default as Button, buttonFrom, buttonsFrom } from './Button';export { default as ButtonGroup } from './ButtonGroup';// ...

csomag.json

// ..."sideEffects": ,// ...

a import { Button } from "@shopify/polaris"; ennek a következő következményei vannak:

  • vegye fel: vegye fel a modult, értékelje és folytassa a függőségek elemzését
  • ugorjon át: ne vegye be, ne értékelje, hanem folytassa a függőségek elemzését
  • kizárja: ne vegye be, ne értékelje, hanem folytassa a függőségek elemzését
  • zárja ki: ne vegye fel értékelje ki és ne elemezze a függőségeket

kifejezetten egyező erőforrás(k) szerint:

  • index.js : Nincs közvetlen exportálás, de sideEffects -> tartalmazza
  • configure.js: nincs exportálás, de sideEffects -> tartalmazza
  • types/index.js: nincs exportálás, nincs megjelölve sideeffects -> kizárja
  • components/index.js: nincs közvetlen exportálás, nincs megjelölve sideeffects-szel, de újraexportált exportál – > ugrás
  • components/Breadcrumbs.js : Nincs exportálás, nincs megjelölve a sideEffects – > zárja ki. Ez kizárta az összes függőséget is components/Breadcrumbs.css még akkor is, ha mellékhatásokkal vannak megjelölve.
  • components/Button.js: közvetlen exportot használ, nincs megjelölve sideEffects -> tartalmazza
  • components/Button.css: nincs exportálás, de megjelölve sideEffects -> tartalmazza

ebben az esetben csak 4 modul szerepel a csomagban:

  • index.js: nagyjából üres
  • configure.js
  • components/Button.js
  • components/Button.css

Ezen optimalizálás után más optimalizálások is alkalmazhatók. Például: buttonFrom és buttonsFrom exportok innen: Button.js is használaton kívül vannak. usedExports az optimalizálás felveszi, és a terser képes lehet néhány utasítást eldobni a modulból.

modul összefűzése is érvényes. Így ez a 4 modul plusz a belépési modul (és valószínűleg több függőség) összefűzhető. index.js nincs kód generálva a végén.

jelöljön meg egy függvényhívást mellékhatásmentesnek

a/*#__PURE__*/ annotáció használatával meg lehet mondani a webpack-nek, hogy egy függvényhívás mellékhatásmentes (tiszta). A funkcióhívások elé helyezhető, hogy mellékhatásmentesként jelölje meg őket. A függvénynek átadott argumentumokat nem jelöli meg az annotáció, ezért lehet, hogy külön kell megjelölni őket. Ha EGY fel nem használt változó kezdeti értékét mellékhatásmentesnek (tiszta) tekintjük, akkor azt Holt kódként jelöljük meg, amelyet a minimalizáló nem hajt végre és elejt. Ez a viselkedés akkor engedélyezett, ha a optimization.innerGraph értéke true.

fájl.js

/*#__PURE__*/ double(55);

kicsinyítse a kimenetet

tehát a import és export szintaxis használatával ki kell dobnunk a”halott kódot”, de még mindig ki kell dobnunk a csomagból. Ehhez állítsa a modekonfigurációs beállítást production értékre.

webcsomag.config.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',};

ezzel a négyzettel futtathatunk egy másik npm run build és megnézhetjük, hogy változott-e valami.

észrevesz valami mást a dist/bundle.js? Nyilvánvaló, hogy az egész csomag most kicsinyített és megcsonkított, de ha alaposan megnézed, nem fogod látni a square függvényt, de látni fogod a cube függvény egy megcsonkított változatát (function r(e){return e*e*e}n.a=r). Kicsinyítéssel és fa rázással a csomagunk most néhány bájttal kisebb! Bár ez nem tűnik soknak ebben a kitalált példában, a fa rázása jelentősen csökkentheti a köteg méretét, ha nagyobb alkalmazásokon dolgozik, összetett függőségi fákkal.

következtetés

tehát, amit megtanultunk, hogy annak érdekében, hogy kihasználják a fa remegés, meg kell…

  • használja az ES2015 modul szintaxisát (azazimport ésexport).
  • győződjön meg arról, hogy az ES2015 modul szintaxisát egyetlen fordító sem alakítja át CommonJS modulokká (ez a népszerű Babel preset @babel/preset-env alapértelmezett viselkedése – további részletekért lásd a dokumentációt).
  • adjon hozzá egy"sideEffects" tulajdonságot a projektpackage.json fájljához.
  • használja aproductionmode konfigurációs lehetőséget a különböző optimalizálások engedélyezéséhez, beleértve a minifikációt és a fa rázását.

el lehet képzelni az alkalmazás, mint egy fa. A forráskód és a ténylegesen használt könyvtárak a fa zöld, élő leveleit képviselik. A Holt kód a fa barna, elhalt leveleit jelenti, amelyeket ősszel fogyasztanak. Annak érdekében, hogy megszabaduljon a halott levelektől, meg kell rázni a fát, aminek következtében leesnek.

ha érdekli a kimenet optimalizálásának további módjai, kérjük, ugorjon a következő útmutatóra a gyártáshoz szükséges építési részletekért.