Zmniejsz rozmiar interfejsu

Jak za pomocą pakietu internetowego zmniejszyć aplikację do rozmiaru

Jedną z pierwszych rzeczy, jakie należy zrobić przy optymalizacji aplikacji, jest jak to tylko możliwe. Oto jak to zrobić w pakiecie internetowym.

Użyj trybu produkcyjnego (tylko pakiet webpack 4)

W pakiecie Webpack 4 wprowadzono nową flagę mode. Tyle możesz ustawić tę flagę na 'development' lub 'production', aby wskazać, że tworzysz pakiet internetowy aplikację do danego środowiska:

// webpack.config.js
module.exports = {
  mode: 'production',
};

Podczas tworzenia aplikacji w wersji produkcyjnej włącz tryb production. Dzięki temu pakiet internetowy będzie stosować optymalizacje takie jak minifikacja czy usuwanie kodu przeznaczonego tylko do programowania w bibliotekach i nie tylko.

Więcej informacji

Włącz minifikację

Minifikacja polega na skompresowaniu kodu poprzez usunięcie dodatkowych spacji, skrócenie nazw zmiennych i i tak dalej. W ten sposób:

// Original code
function map(array, iteratee) {
  let index = -1;
  const length = array == null ? 0 : array.length;
  const result = new Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

// Minified code
function map(n,r){let t=-1;for(const a=null==n?0:n.length,l=Array(a);++t<a;)l[t]=r(n[t],t,n);return l}

Webpack obsługuje 2 sposoby minifikacji kodu: minifikację na poziomie pakietu i opcji modułu ładowania. Należy ich używać jednocześnie.

Minimalizacja na poziomie pakietu

Minimalizacja na poziomie pakietu skompresuje cały pakiet po kompilacji. Działa to w następujący sposób:

  1. Piszesz kod w taki sposób:

    // comments.js
    import './comments.css';
    export function render(data, target) {
      console.log('Rendered!');
    }
    
  2. Webpack kompiluje go do mniej więcej takiego kodu:

    // bundle.js (part of)
    "use strict";
    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
    /* harmony export (immutable) */ __webpack_exports__["render"] = render;
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css__ = __webpack_require__(1);
    /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__comments_css_js___default =
    __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__comments_css__);
    
    function render(data, target) {
    console.log('Rendered!');
    }
    
  3. Minimalizator kompresuje go do mniej więcej takiej postaci:

    // minified bundle.js (part of)
    "use strict";function t(e,n){console.log("Rendered!")}
    Object.defineProperty(n,"__esModule",{value:!0}),n.render=t;var o=r(1);r.n(o)
    

W pakiecie webpack 4 minifikacja na poziomie pakietu jest włączona automatycznie – zarówno w środowisku produkcyjnym, i bez żadnego trybu. Wykorzystuje minifikator UglifyJS. Jeśli kiedykolwiek zechcesz wyłączyć minifikację, użyj trybu programisty lub przekaż false do opcji optimization.minimize).

W pakiecie webpack 3 musisz użyć wtyczki UglifyJS. bezpośrednio. Wtyczka jest dostarczana z pakietem webpack, aby ją włączyć, dodaj ją do plugins konfiguracji:

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.UglifyJsPlugin(),
  ],
};

Opcje dotyczące modułu ładowania

Drugim sposobem na zminimalizowanie kodu są opcje dla konkretnego narzędzia (jakie narzędzie jest). Dzięki opcjom wczytywania możesz skompresować elementy, nie można minifikować. Na przykład: gdy zaimportujesz plik CSS z css-loader, plik jest skompilowany w ciąg znaków:

/* comments.css */
.comment {
  color: black;
}
// minified bundle.js (part of)
exports=module.exports=__webpack_require__(1)(),
exports.push([module.i,".comment {\r\n  color: black;\r\n}",""]);

minifikator nie może skompresować tego kodu, ponieważ jest to ciąg znaków. Aby zmniejszyć zawartość pliku, musimy skonfiguruj ładowanie:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          { loader: 'css-loader', options: { minimize: true } },
        ],
      },
    ],
  },
};

Więcej informacji

Podaj NODE_ENV=production

Innym sposobem na zmniejszenie rozmiaru interfejsu jest ustawienie NODE_ENV zmienna środowiskowa w kodzie na wartość production.

Biblioteki odczytują zmienną NODE_ENV, aby określić, w jakim trybie powinny działać – w polu w środowisku programistycznym lub produkcyjnym. Niektóre biblioteki działają inaczej w zależności od tej zmiennej. Dla: Jeśli na przykład NODE_ENV nie ma wartości production, Vue.js przeprowadzi dodatkowe testy i wydruki ostrzeżenia:

// vue/dist/vue.runtime.esm.js
// …
if (process.env.NODE_ENV !== 'production') {
  warn('props must be strings when using array syntax.');
}
// …

React działa podobnie – wczytuje kompilację deweloperską, która zawiera ostrzeżenia:

// react/index.js
if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

// react/cjs/react.development.js
// …
warning$3(
    componentClass.getDefaultProps.isReactClassApproved,
    'getDefaultProps is only used on classic React.createClass ' +
    'definitions. Use a static property named `defaultProps` instead.'
);
// …

Takie kontrole i ostrzeżenia są zwykle niepotrzebne w środowisku produkcyjnym, ale pozostają w kodzie zwiększyć rozmiar biblioteki. W pakiecie webpack 4 usuń je,dodając opcję optimization.nodeEnv: 'production':

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    nodeEnv: 'production',
    minimize: true,
  },
};

W pakiecie webpack 3 zamiast tego użyj DefinePlugin:

// webpack.config.js (for webpack 3)
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': '"production"'
    }),
    new webpack.optimize.UglifyJsPlugin()
  ]
};

Opcja optimization.nodeEnv i DefinePlugin działają tak samo – zastępują one wszystkie wystąpienia process.env.NODE_ENV określoną wartością. Za pomocą konfiguracji z góry streszczenie:

  1. Webpack zastąpi wszystkie wystąpienia tekstu process.env.NODE_ENV ciągiem "production":

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if (process.env.NODE_ENV !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if ("production" !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    
  2. minifikator usunie wszystkie takie treści, gałęzi if – ponieważ "production" !== 'production' ma zawsze wartość fałsz, a wtyczka rozumie, że kod zawarty w tych gałęziach nigdy nie zostanie wykonany:

    // vue/dist/vue.runtime.esm.js
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    } else if ("production" !== 'production') {
      warn('props must be strings when using array syntax.');
    }
    

    // vue/dist/vue.runtime.esm.js (without minification)
    if (typeof val === 'string') {
      name = camelize(val);
      res[name] = { type: null };
    }
    

Więcej informacji

Korzystanie z modułów ES

Następnym sposobem na zmniejszenie rozmiaru interfejsu jest użycie ES .

Gdy korzystasz z modułów ES, Webpack może potrząsać drzewami. Trzęsienie drzewem ma miejsce, gdy przegląda całe drzewo zależności, sprawdza używane zależności i usuwa nieużywane. A więc, Jeśli używasz składni modułu ES, pakiet internetowy może wyeliminować nieużywany kod:

  1. Gdy tworzysz plik z wieloma eksportami, aplikacja używa tylko jednego z nich:

    // comments.js
    export const render = () => { return 'Rendered!'; };
    export const commentRestEndpoint = '/rest/comments';
    
    // index.js
    import { render } from './comments.js';
    render();
    
  2. Webpack rozumie, że interfejs commentRestEndpoint nie jest używany i nie generuje osobny punkt eksportu w pakiecie:

    // bundle.js (part that corresponds to comments.js)
    (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    const render = () => { return 'Rendered!'; };
    /* harmony export (immutable) */ __webpack_exports__["a"] = render;
    
    const commentRestEndpoint = '/rest/comments';
    /* unused harmony export commentRestEndpoint */
    })
    
  3. minifikator usuwa nieużywaną zmienną:

    // bundle.js (part that corresponds to comments.js)
    (function(n,e){"use strict";var r=function(){return"Rendered!"};e.b=r})
    

Działa to nawet w przypadku bibliotek napisanych za pomocą modułów ES.

Nie musisz jednak używać wbudowanego minifikera pakietu webpacka (UglifyJsPlugin). dowolne minifikatory obsługujące usuwanie martwych kodów, (np. wtyczka Babel Minify lub wtyczka Google Closure Compiler). poradzi sobie z tym.

Więcej informacji

Zoptymalizuj obrazy

Obrazy stanowią więcej niż do połowy rozmiaru strony. Choć nie są tak ważne jak JavaScript (np. nie blokują renderowania), ale i tak pochłaniają znaczną część przepustowość. Użyj komponentów url-loader, svg-url-loader i image-webpack-loader, aby je zoptymalizować w Webpack.

url-loader wstawia małe pliki statyczne w . Bez konfiguracji pobiera przesłany plik, umieszcza go obok skompilowanego pakietu i zwrotów . Jeśli jednak określisz opcję limit, zakoduje ona pliki mniejsze niż ten limit jako adres URL danych w Base64 i zwraca ten URL. Ten wbudowuje obraz w kod JavaScript i zapisuje żądanie HTTP:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif)$/,
        loader: 'url-loader',
        options: {
          // Inline files smaller than 10 kB (10240 bytes)
          limit: 10 * 1024,
        },
      },
    ],
  }
};
// index.js
import imageUrl from './image.png';
// → If image.png is smaller than 10 kB, `imageUrl` will include
// the encoded image: 'data:image/png;base64,iVBORw0KGg…'
// → If image.png is larger than 10 kB, the loader will create a new file,
// and `imageUrl` will include its url: `/2fcd56a1920be.png`

svg-url-loader działa tak samo jak url-loader – z tą różnicą, że koduje pliki z adresem URL zamiast Base64. jeden. Przydaje się to w przypadku obrazów SVG – ponieważ pliki SVG mają postać zwykłego tekstu, to kodowanie jest na większą oszczędność.

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        loader: "svg-url-loader",
        options: {
          limit: 10 * 1024,
          noquotes: true
        }
      }
    ]
  }
};

image-webpack-loader kompresuje obrazy, które są przesyłane i przechodzić przez niego. Plik obsługuje obrazy JPG, PNG, GIF i SVG, więc będziemy go używać we wszystkich tych formatach.

Ten program wczytujący nie jest osadzony w aplikacji, dlatego musi działać w parze z url-loader oraz svg-url-loader Aby uniknąć kopiowania i wklejania do obu reguł (jedna dla obrazów JPG, PNG lub GIF, a druga jeden w przypadku plików SVG), dodamy ten program ładujący jako osobną regułę z tagiem enforce: 'pre':

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/,
        loader: 'image-webpack-loader',
        // This will apply the loader before the other ones
        enforce: 'pre'
      }
    ]
  }
};

Domyślne ustawienia programu ładującego są gotowe, ale jeśli chcesz je skonfigurować przeczytaj opcje wtyczki. Do wybierz opcje, które chcesz określić, przeczytaj doskonały przewodnik po zdjęciach Addy'ego Osmaniego

Więcej informacji

Optymalizacja zależności

Ponad połowa średniego rozmiaru języka JavaScript pochodzi z zależności, a część tego rozmiaru może być po prostu niepotrzebne.

Na przykład Lodash (wersja 4.17.4) dodaje do pakietu 72 KB zminifikowanego kodu. Ale jeśli używasz tylko jednej funkcji, np. 20 metod, to około 65 KB zminifikowanego kodu nie daje żadnych efektów.

Innym przykładem jest Moment.js. Jej wersja 2.19.1 zajmuje 223 KB zminifikowanego kodu, co jest imponujące. średni rozmiar pliku JavaScript na stronie wynosił w październiku 452 KB 2017 r. Jednak 170 KB tego rozmiaru to lokalizacja . Jeśli nie używasz Moment.js w wielu językach, te pliki powiększą pakiet bez cel.

Wszystkie te zależności można łatwo zoptymalizować. Zebraliśmy strategie optymalizacji repozytorium GitHub – sprawdź je

Włącz konkatenację modułów dla modułów ES (inaczej podnoszenie zakresu)

Podczas tworzenia pakietu pakiet internetowy opakowuje każdy moduł w jedną funkcję:

// index.js
import {render} from './comments.js';
render();

// comments.js
export function render(data, target) {
  console.log('Rendered!');
}

// bundle.js (part  of)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
  var __WEBPACK_IMPORTED_MODULE_0__comments_js__ = __webpack_require__(1);
  Object(__WEBPACK_IMPORTED_MODULE_0__comments_js__["a" /* render */])();
}),
/* 1 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  __webpack_exports__["a"] = render;
  function render(data, target) {
    console.log('Rendered!');
  }
})

W przeszłości konieczne było odizolowanie od siebie modułów CommonJS/AMD. W ten sposób udało się jednak dodać a także większy nakład pracy, dla każdego modułu.

W pakiecie Webpack 2 wprowadzono obsługę modułów ES. W przeciwieństwie do modułów CommonJS i AMD można je łączyć w pakiety. bez dodawania każdej z tych funkcji. Połączenie pakietu Webpack 3 było możliwe. konkatenacji modułów. Oto Jak działa konkatenacja modułów:

// index.js
import {render} from './comments.js';
render();

// comments.js
export function render(data, target) {
  console.log('Rendered!');
}

// Unlike the previous snippet, this bundle has only one module
// which includes the code from both files

// bundle.js (part of; compiled with ModuleConcatenationPlugin)
/* 0 */
(function(module, __webpack_exports__, __webpack_require__) {
  "use strict";
  Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

  // CONCATENATED MODULE: ./comments.js
    function render(data, target) {
    console.log('Rendered!');
  }

  // CONCATENATED MODULE: ./index.js
  render();
})

Widzisz różnicę? W zwykłym pakiecie moduł 0 wymagał parametru render od modułu 1. Na konkatenacji modułów, funkcja require zostaje po prostu zastąpiona wymaganą funkcją, a moduł 1 usunięto. Pakiet składa się z mniejszej liczby modułów i mniejszego nakładu pracy.

Aby włączyć to zachowanie, w pakiecie webpack 4 włącz opcję optimization.concatenateModules:

// webpack.config.js (for webpack 4)
module.exports = {
  optimization: {
    concatenateModules: true
  }
};

W pakiecie webpack 3 użyj ModuleConcatenationPlugin:

// webpack.config.js (for webpack 3)
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
  ]
};

Więcej informacji

Użyj kodu externals, jeśli masz zarówno kod pakietu internetowego, jak i innego

W dużych projektach część kodu jest skompilowana z pakietem webpack, a część nie. Polub witryny hostującej filmy, w której widżet odtwarzacza może być utworzony za pomocą pakietu internetowego, może nie:

Zrzut ekranu witryny hostującej filmy
(całkowicie losowa witryna z filmami)

Jeśli oba fragmenty kodu mają wspólne zależności, możesz je udostępnić, aby uniknąć pobierania ich kodu wiele razy. Można to zrobić za pomocą parametru externals pakietu internetowego opcja – zastępuje moduły ze zmiennymi lub importowanie danych spoza domeny.

Jeśli zależności są dostępne w: window

Jeśli Twój kod inny niż pakiet internetowy zależy od zależności, które są dostępne jako zmienne w window, alias nazwy zależności do nazw zmiennych:

// webpack.config.js
module.exports = {
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  }
};

W przypadku tej konfiguracji pakiet internetowy nie zawiera pakietów react i react-dom. Będą natomiast :

// bundle.js (part of)
(function(module, exports) {
  // A module that exports `window.React`. Without `externals`,
  // this module would include the whole React bundle
  module.exports = React;
}),
(function(module, exports) {
  // A module that exports `window.ReactDOM`. Without `externals`,
  // this module would include the whole ReactDOM bundle
  module.exports = ReactDOM;
})

Jeśli zależności są wczytywane jako pakiety AMD

Jeśli Twój kod inny niż pakiet internetowy nie ujawnia zależności w klastrze window, sytuacja jest bardziej skomplikowana. Nadal jednak można uniknąć dwukrotnego wczytywania tego samego kodu, jeśli kod spoza pakietu internetowego je wykorzysta. jako pakiety AMD.

W tym celu skompiluj kod pakietu internetowego jako pakiet AMD i moduły aliasów dla adresów URL bibliotek:

// webpack.config.js
module.exports = {
  output: {
    libraryTarget: 'amd'
  },
  externals: {
    'react': {
      amd: '/libraries/react.min.js'
    },
    'react-dom': {
      amd: '/libraries/react-dom.min.js'
    }
  }
};

Webpack spakuje pakiet w element define() i sprawi, że będzie zależał od tych adresów URL:

// bundle.js (beginning)
define(["/libraries/react.min.js", "/libraries/react-dom.min.js"], function () { … });

Jeśli kod inny niż pakiet internetowy używa tych samych adresów URL do wczytywania zależności, te pliki zostaną wczytane tylko raz – dodatkowe żądania będą korzystać z pamięci podręcznej wczytywania.

Więcej informacji

Podsumowanie

  • Jeśli używasz pakietu Webpack 4, włącz tryb produkcyjny
  • Zminimalizuj kod dzięki opcjom minifikatora i programu wczytującego na poziomie pakietu
  • Usuń kod tylko dla programistów, zastępując NODE_ENV kodem production
  • Użyj modułów ES, aby umożliwić potrząsanie drzewem
  • Skompresuj obrazy
  • Zastosuj optymalizacje dostosowane do zależności
  • Włącz konkatenację modułów
  • Jeśli uważasz, że to może być przydatne, użyj metody externals