Articles

Tre Risting

tre risting er et begrep som vanligvis brukes i JavaScript sammenheng for død-kode eliminering. Det er avhengig AV statisk struktur AV ES2015 modul syntaks, dvs. import og export. Navnet og konseptet har blitt popularisert AV ES2015-modulen bundler rollup.webpack 2 utgivelsen kom med innebygd støtte FOR ES2015 moduler (alias harmony moduler) samt ubrukt modul eksport deteksjon. Den nye webpack 4 utgivelsen utvider på denne evnen med en måte å gi hint til kompilatoren via "sideEffects"package.json egenskapen å betegne hvilke filer i prosjektet er «ren» og derfor trygt å beskjære hvis ubrukt.

Legg Til Et Verktøy

La oss legge til en ny verktøyfil i prosjektet vårt, src/math.js, som eksporterer to funksjoner:

prosjekt

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

Settmode konfigurasjonsalternativet til utvikling for å sikre at bunten ikke er minifisert:

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

med det på plass, la oss oppdatere vårt oppføringsskript for å utnytte en av disse nye metodene og fjerne lodash for enkelhet:

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

Merk at vi Ikke importsquare metoden frasrc/math.js modulen. Den funksjonen er det som kalles «død kode», noe som betyr en ubrukt export som skal slippes. La oss nå kjøre vårt npm-skript, npm run build, og inspisere utgangspakken:

dist / bundle.js (rundt linjene 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; }});

Merk unused harmony export square kommentar ovenfor. Hvis du ser på koden under den, vil du legge merke til at square ikke importeres, men det er fortsatt inkludert i bunten. Vi vil fikse det i neste avsnitt.

Merk filen som bivirkningsfri

i en 100% ESM-modulverden er det enkelt å identifisere bivirkninger. Vi er imidlertid ikke der ennå, så i mellomtiden er det nødvendig å gi tips til webpacks kompilator på «renheten» av koden din.

måten dette oppnås på er"sideEffects" pakken.json eiendom.

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

all koden nevnt ovenfor inneholder ikke bivirkninger, så vi kan bare merke eiendommen som false for å informere webpack om at den trygt kan beskjære ubrukt eksport.

hvis koden din hadde noen bivirkninger, kan en matrise gis i stedet:

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

matrisen aksepterer enkle globemønstre til de relevante filene. Den bruker glob-til-regexp under panseret (Støtter: ***{a,b}). Mønstre som *.css, som ikke inkluderer en /, vil bli behandlet som **/*.css.

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

endelig "sideEffects" kan også settes framodule.rules konfigurasjonsalternativ.

Avklare tre risting og sideEffects

den sideEffects og usedExports (mer kjent som tre risting) optimaliseringer er to forskjellige ting.

sideEffects er mye mer effektivt siden det tillater å hoppe over hele moduler/filer og hele undertreet.

usedExports er avhengig av terser for å oppdage bivirkninger i uttalelser. Det er en vanskelig oppgave I JavaScript og ikke så effektiv som greisideEffects flagg. Det kan heller ikke hoppe over subtre / avhengigheter siden spec sier at bivirkninger må evalueres. Mens eksportfunksjonen fungerer fint, Er React ‘ S Higher Order Components (HOC) problematiske i denne forbindelse.

la oss lage et eksempel:

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

den forhåndspakkede versjonen ser slik ut:

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

når Button er ubrukt, kan du effektivt fjerne export { Button$1 }; som etterlater all gjenværende kode. Så spørsmålet er «Har denne koden noen bivirkninger, eller kan den fjernes trygt?». Vanskelig å si, spesielt på grunn av denne linjen withAppProvider()(Button)withAppProvider kalles og returverdien kalles også. Er det noen bivirkninger når du ringer merge eller hoistStatics? Er det bivirkninger ved tildeling av WithProvider.contextTypes(Setter?) eller når du leser WrappedComponent.contextTypes (Getter?).

Terer prøver faktisk å finne ut det, men det vet ikke sikkert i mange tilfeller. Dette betyr ikke at terer ikke gjør jobben sin bra fordi den ikke kan finne ut det. Det er bare for vanskelig å fastslå det pålitelig på et dynamisk språk som JavaScript.

Men Vi kan hjelpe terser ved å bruke/*#__PURE__*/ annotasjon. Det flagger en uttalelse som bivirkning gratis. Så en enkel endring vil gjøre det mulig å tre-riste koden:

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

dette vil tillate å fjerne dette kodestykket. Men det er fortsatt spørsmål med importen som må inkluderes/evalueres fordi de kan inneholde bivirkninger.

for å takle dette bruker vi "sideEffects" egenskapen i package.json.

det ligner på /*#__PURE__*/ men på et modulnivå i stedet for et setningsnivå. Det står ("sideEffects" property): «hvis ingen direkte eksport fra en modul flagget med no-sideEffects brukes, bundler kan hoppe evaluere modulen for bivirkninger.».

i Shopify ‘ S Polaris-eksempel ser originale moduler slik ut:

indeks.js

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

komponenter / indeks.js

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

pakke.json

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

for import { Button } from "@shopify/polaris"; dette har følgende implikasjoner:

  • inkluder det: inkluder modulen, evaluer den og fortsett å analysere avhengigheter
  • hopp over: ikke inkluder den, ikke evaluer den, men fortsett å analysere avhengigheter
  • ekskluder den: ikke inkluder den, ikke evaluer det og analyser ikke avhengigheter

spesifikt per matchende ressurs(er):

  • index.js: Ingen direkte eksport brukes, men flagget med sideEffects -> inkluder det
  • configure.js: ingen eksport brukes, men flagget med sideEffects -> inkluder det
  • types/index.js: ingen eksport brukes, ikke flagget med sideeffects -> ekskluder den
  • components/index.js: ingen direkte eksport brukes, ikke flagget med sideeffects, men reexported eksport brukes -> hopp over
  • components/Breadcrumbs.js: Ingen eksport brukes, ikke flagget med sideEffects- > ekskluder det. Dette utelukket også alle avhengigheter som components/Breadcrumbs.css selv om de er flagget med sideEffects.
  • components/Button.js: Direkte eksport brukes, ikke flagget med sideEffects ->inkluder det

  • components/Button.css: ingen eksport brukes, men flagget med sideEffects ->inkluder det

i dette tilfellet er bare 4 moduler inkludert i bunten:

  • index.js: ganske mye tom
  • configure.js
  • components/Button.jscomponents/Button.css

etter denne optimaliseringen kan andre optimaliseringer fortsatt gjelde. For eksempel:buttonFrom ogbuttonsFrom eksporter fraButton.js er også ubrukte. usedExports optimalisering vil plukke den opp og terser kan kanskje slippe noen uttalelser fra modulen.

Modul Sammenkobling gjelder også. Slik at disse 4 modulene pluss inngangsmodulen (og sannsynligvis flere avhengigheter) kan sammenkobles. index.js har ingen kode generert til slutt.

Merk et funksjonskall som bivirkningsfritt

det er mulig å fortelle webpack at et funksjonskall er bivirkningsfritt (rent) ved å bruke/*#__PURE__*/ merknad. Det kan settes foran funksjonssamtaler for å markere dem som bivirkning-fri. Argumenter som sendes til funksjonen, blir ikke merket av merknaden og må kanskje merkes individuelt. Når den opprinnelige verdien i en variabel erklæring av en ubrukt variabel anses som bivirkning-fri (ren), er det å bli merket som død kode, ikke utført og falt av minimizer. Denne virkemåten er aktivert når optimization.innerGraph er satt til true.

fil.js

/*#__PURE__*/ double(55);

Minify Utgangen

så vi har cued opp vår «døde kode» for å bli droppet ved å brukeimport ogexport syntaks, men vi trenger fortsatt å slippe den fra bunten. For å gjøre det, settmode konfigurasjonsalternativet til production.

webpack.konfig.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',};

med det kvadrerte kan vi kjøre en annen npm run build og se om noe har endret seg.

Legg merke til noe annet om dist/bundle.js? Klart er hele bunten nå minified og manglet, men hvis du ser nøye ut, vil du ikke se square – funksjonen inkludert, men vil se en manglet versjon av cube – funksjonen (function r(e){return e*e*e}n.a=r). Med minification og tre risting, er vår bunt nå noen byte mindre! Selv om det kanskje ikke virker som mye i dette konstruerte eksemplet, kan tre risting gi en betydelig reduksjon i buntstørrelse når du arbeider med større applikasjoner med komplekse avhengighetstrær.

Konklusjon

Så, det vi har lært er at for å dra nytte av tre risting, må du…

  • Bruk ES2015 modul syntaks (dvs. import ogexport).
  • Pass på at ingen kompilatorer forvandler DIN ES2015 – modulsyntaks til CommonJS-moduler(dette er standard oppførsel av Den populære Babel preset @babel / preset-env – se dokumentasjonen for flere detaljer).
  • Legg til en "sideEffects" egenskap til prosjektetspackage.json fil.
  • Brukproductionmode konfigurasjonsalternativ for å aktivere ulike optimaliseringer, inkludert minifisering og tre risting.

du kan forestille deg søknaden din som et tre. Kildekoden og bibliotekene du faktisk bruker representerer de grønne, levende blader av treet. Død kode representerer de brune, døde bladene på treet som forbrukes om høsten. For å bli kvitt de døde bladene, må du riste treet, forårsaker dem til å falle.

hvis du er interessert i flere måter å optimalisere produksjonen, kan du hoppe til neste guide for detaljer om bygg for produksjon.