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 import
square
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 -
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, kutencomponents/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
configure.js
: vientiä ei käytetä, mutta merkitään sivuefekteillä ->sisältää sen
tässä tapauksessa nipussa on mukana vain 4 moduulia:
-
index.js
: aika tyhjäksi -
components/Button.js
configure.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
jaexport
). - 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 projektisipackage.json
tiedostoon. - käytä
production
mode
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.