Articles

puiden ravistelu

puiden ravistelu on JavaScript-kontekstissa yleisesti käytetty termi dead-code-eliminoinnille. Se nojaa ES2015-moduulin syntaksin staattiseen rakenteeseen eli import ja export. Nimen ja konseptin on tehnyt suosituksi ES2015-moduulin bundler rollup.

webpack 2-julkaisun mukana tuli sisäänrakennettu tuki ES2015-moduuleille (alias harmony modules) sekä käyttämättömien moduulien viennin tunnistus. Uusi webpack 4-julkaisu laajentaa tätä ominaisuutta antamalla vihjeitä kääntäjälle "sideEffects"package.json ominaisuus, joka osoittaa mitkä tiedostot projektissasi ovat” puhtaita ” ja siten turvallisia karsia käyttämättöminä.

lisää apuohjelma

lisätään projektiin uusi yleishyödyllinen tiedosto, src/math.js, joka vie kaksi funktiota:

projekti

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

Aseta mode konfigurointivaihtoehto kehitykseen, jotta varmistat, ettei nippu ole minifioitu:

webpack.määritys.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,+ },};

kun se on paikallaan, päivitetäänpä syöttökomennuksemme hyödyntämään yhtä näistä uusista menetelmistä ja poistetaan lodash yksinkertaisuuden vuoksi:

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

huomaa, että emme importsquare menetelmä src/math.js moduuli. Kyseinen funktio on niin sanottu” kuollut koodi”, eli käyttämätön export, joka pitäisi pudottaa. Nyt ajetaan npm-skripti, npm run build ja tarkastetaan tulostuspaketti:

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

huomaa unused harmony export square kommentti yllä. Jos sen alla olevaa koodia katsoo, huomaa, että square ei tuoda, mutta se on edelleen mukana nipussa. Korjaamme sen seuraavassa jaksossa.

merkitse tiedosto sivuvaikutuksettomaksi

100% ESM-moduulimaailmassa, sivuvaikutusten tunnistaminen on helppoa. Kuitenkin, emme ole siellä aivan vielä, joten sillä välin on tarpeen antaa vihjeitä webpackin kääntäjä ”puhtauden” koodin.

tämän toteutustapa on "sideEffects" paketti.JSONin kiinteistö.

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

kaikki edellä mainittu koodi ei sisällä sivuvaikutuksia, joten voimme yksinkertaisesti merkitä ominaisuuden muotoon false ilmoittaaksemme webpackille, että se voi turvallisesti karsia käyttämättömän viennin.

jos koodilla oli kuitenkin joitain sivuvaikutuksia, voidaan sen sijaan antaa taulukko:

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

ryhmä hyväksyy yksinkertaiset glob-kuviot asiaankuuluviin tiedostoihin. Se käyttää hupun alla glob-to-regexpiä (tukee: ***{a,b}). Malleja, kuten *.css, jotka eivät sisällä /, käsitellään kuten **/*.css.

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

lopulta "sideEffects" voidaan asettaa myös module.rules asetusvaihtoehdosta.

selventävä puun ravistelu ja sivuefektit

sideEffects ja usedExports (tunnetaan paremmin nimellä puun ravistelu) optimoinnit ovat kaksi eri asiaa.

sideEffects on paljon tehokkaampi, koska sen avulla voidaan ohittaa kokonaisia moduuleja / tiedostoja ja täydellinen alatreemi.

usedExports käyttää terseriä sivuvaikutusten havaitsemiseen lausunnoissa. Se on Javascriptissä vaikea tehtävä eikä yhtä tehokas kuin suoraviivainen sideEffects lippu. Se ei myöskään voi ohittaa subtree / riippuvuuksia, koska spec sanoo, että haittavaikutukset on arvioitava. Vaikka funktion vieminen toimii hyvin, Reactin korkeamman kertaluvun komponentit (HOC) ovat tässä suhteessa ongelmallisia.

tehdäänpä esimerkki:

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

valmiiksi niputettu versio näyttää tältä:

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

kun Button on käyttämätön, voit poistaa export { Button$1 };, jolloin jäljelle jää koko koodi. Joten kysymys on ” onko tällä koodilla mitään sivuvaikutuksia tai voidaan turvallisesti poistaa?”. Vaikea sanoa, varsinkin tämän viivan vuoksi withAppProvider()(Button)withAppProvider kutsutaan ja myös palautusarvoa kutsutaan. Onko merge tai hoistStatics? Onko WithProvider.contextTypes (Setter?) tai lukiessa WrappedComponent.contextTypes (Getter?).

Terser yrittää itse asiassa selvittää asiaa, mutta se ei monesti tiedä varmasti. Tämä ei tarkoita, että terser ei tee työnsä hyvin, koska se ei voi selvittää sitä. Se on vain liian vaikea määrittää luotettavasti dynaaminen kieli kuten JavaScript.

mutta voimme auttaa terseriä käyttämällä /*#__PURE__*/ – merkintää. Se merkitsee lausunnon sivuvaikutuksetta. Yksinkertainen muutos mahdollistaisi siis koodin puuduttamisen:

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

näin tämä koodinpätkä voitaisiin poistaa. Mutta on vielä kysymyksiä tuonnin kanssa, jotka on sisällytettävä / arvioitava, koska ne voivat sisältää sivuvaikutuksia.

tähän puututaan käyttämällä "sideEffects" omaisuutta package.json.

se on samanlainen kuin /*#__PURE__*/, mutta modulitasolla lausetason sijaan. Siinä lukee ("sideEffects" property): ”jos ei käytetä suoraa vientiä moduulista, jossa ei ole sivuvaikutuksia, Niputtaja voi ohittaa moduulin arvioinnin sivuvaikutusten varalta.”.

Shopify ’ n Polaris-esimerkissä alkuperäiset moduulit näyttävät tältä:

indeksi.js

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

komponentit/indeksi.js

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

paketti.json

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

For import { Button } from "@shopify/polaris"; this has the following implications:

  • include it: include the module, evaluate it and continue analysing dependencies
  • skip over: don ’t include it, don’ t evaluate it but continue analysing dependencies
  • exclude it: don ’t include it don’ t evaluate it and don ’ t Analyse dependencies

specifically per matching resource(s):

  • index.js: Suoraa vientiä ei käytetä, vaan merkitään sivuefekteillä -> include it
  • configure.js: vientiä ei käytetä, mutta merkitään sivuefekteillä ->sisältää sen

  • types/index.js: ei käytetä vientiä, ei merkitä sivuefekteillä ->poissulje se
  • components/index.js: ei käytetä suoraa vientiä, ei merkitä sivuefekteillä, mutta jälleenvientiä käytetään ->skip over
  • components/Breadcrumbs.js: Vientiä ei käytetä, ei merkitä sivuefekteillä -> sulje se pois. Tämä jätti ulkopuolelle myös kaikki riippuvuudet, kuten components/Breadcrumbs.css, vaikka niihin merkittäisiin sivuefektit.
  • components/Button.js: käytetään suoraa vientiä, johon ei ole merkitty sivuefektejä -> sisältää sen
  • components/Button.css: ei käytetä vientiä, vaan merkitään sivuefekteillä -> include it

tässä tapauksessa nipussa on mukana vain 4 moduulia:

  • index.js: aika tyhjäksi
  • configure.js

  • components/Button.js
  • components/Button.css

tämän optimoinnin jälkeen voidaan vielä soveltaa muita optimointeja. Esimerkiksi: buttonFrom ja buttonsFrom vienti Button.js ovat myös käyttämättömiä. usedExports optimointi poimii sen ylös ja terser saattaa pudottaa joitain lausekkeita moduulista.

myös moduulien yhdistämistä sovelletaan. Niin, että nämä 4 moduulit plus merkintä moduuli (ja luultavasti enemmän riippuvuuksia) voidaan yhdistää. index.js ei ole lopussa muodostettua koodia.

merkitse funktiokutsu sivuvaikutuksettomaksi

on mahdollista kertoa webpackille funktiokutsun olevan sivuvaikutukseton (puhdas) käyttämällä /*#__PURE__*/ merkintää. Se voidaan laittaa funktiokutsujen eteen merkitsemään ne sivuvaikutuksettomiksi. Funktiolle siirrettyjä argumentteja ei merkitä merkinnällä ja ne voidaan joutua merkitsemään yksittäin. Kun alkuperäisen arvon muuttuja ilmoitus käyttämättömän muuttujan pidetään sivuvaikutuksetta vapaa (puhdas), se on saada merkitty kuollut koodi, ei suoriteta ja pudotetaan minimoija. Tämä käyttäytyminen on käytössä, kun optimization.innerGraph on asetettu true.

tiedosto.js

/*#__PURE__*/ double(55);

Pienennä ulostulo

joten olemme kasanneet ”kuolleen koodimme” pudotettavaksi käyttämällä import ja export syntaksia, mutta se on vielä pudotettava nipusta. Voit tehdä sen asettamalla mode asetusvaihtoehdoksi production.

webpack.määritys.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',};

sillä potenssilla voidaan ajaa toinen npm run build ja katsoa, onko mikään muuttunut.

Huomaatko mitään erilaista dist/bundle.js? Selvästi koko nippu on nyt miinoitettu ja mankeloitu, mutta jos katsoo tarkkaan, ei näe square funktio mukana, vaan näkee mankeloidun version cube funktio (function r(e){return e*e*e}n.a=r). Miinoituksella ja puun ravistelulla, meidän nippu on nyt muutaman tavun pienempi! Vaikka tämä ei ehkä tunnu paljolta tässä keksityssä esimerkissä, puiden ravistelu voi vähentää merkittävästi pakettikokoa työskenneltäessä suuremmissa sovelluksissa, joissa on monimutkaisia riippuvuuspuita.

johtopäätös

niin, mitä olemme oppineet on, että jotta puun ravistelua voi hyödyntää, on pakko…

  • käyttävät ES2015-moduulien syntaksia (eli import ja export).
  • varmista, etteivät kääntäjät muuta ES2015-moduulin syntaksia CommonJS – moduuleiksi (tämä on suositun Babel Presetin @babel / preset-env-oletuskäyttäytyminen-katso lisätietoja ohjeista).
  • lisää "sideEffects" ominaisuus projektisi package.json tiedostoon.
  • käytä productionmode asetusvaihtoehtoa mahdollistaaksesi erilaiset optimoinnit, kuten miinoittamisen ja puun ravistelun.

voit kuvitella hakemuksesi puuksi. Käyttämäsi lähdekoodi ja kirjastot edustavat puun vihreitä, eläviä lehtiä. Kuollut koodi edustaa syksyn kuluttamia ruskeita, kuolleita puun lehtiä. Jotta päästä eroon kuolleista lehdistä, sinun täytyy ravistella puuta, jolloin ne putoavat.

Jos olet kiinnostunut useammista tavoista optimoida tuotoksesi, hyppää seuraavaan oppaaseen saadaksesi lisätietoja tuotannon rakentamisesta.