Основи Webpack 2

27 грудня 2016 15:08 OlegWock 1221 0

Що таке Webpack?

Якщо просто, то це пакувальник модулів для вашого JavaScript. Але з часів першого релізу він еволюціонував в пакувальник для всього вашого фронтенду.

Ось так це було раніше: розмітка, стилі так код ізольовані, ви повинні керувати кожним окремо.

Таск ранери, такі як Gulp, можуть працювати з купою різних пре- та постпроцесорів, компіляторів та транспіляторів, але принцип роботи один і той же завжди: ви даєте якийсь вхідний код, і отримуєте вихідний. Він робить це ізольовано, не піклуючись про систему в цілому. Тому завданням розробника стає зібрати всі частинки після Gulp в робочий продакшн-код.

Webpack намагається трохи допомогти розробнику і забрати частину його роботи, задаючи сміливе питання: "а якщо буде така частина процесу розробки, яка сама збиратиме залежності? А якщо нам було б потрібно просто писати код, який збирається сам, на основі того, що ми хочемо отримати?".

Webpack way: пакуємо тільки те, що дійсно потрібне в продакшені.

Якщо ви в веб-спільноті декілька років, то знаєте що всі проблеми вирішують за допомогою JavaScript. Webpack притримується цієї філософії, тому все управління залежностями відбувається за допомогою JS. Але сильною стороною Webpack є не це, а те, що прошарок управління цілком написаний на JavaScript (з фічами Node).

Інакше кажучи, ви не пишете код для Webpack. Ви пишете код для вашого проекту, а Webpack робить все сам (з деякими конфігами, звісно).

Якщо в вас була хоча б одна з них проблем:

  • Включення залежностей не в тому порядку.

  • Включення не використовуваного JS та CSS в продакшн-код.

  • Завантаження тих самих бібліотек декілька разів.

  • Проблеми з просторами імен.

  • Хочеться покращити систему збирання, але боїтесь, що щось зламаєте.

... то вам повинен сподобатися Webpack. Він передає вирішення всіх цих проблем до JavaScript'у. І саме прекрасне — він може запускатися на бекенді.

Перші кроки

Ми будемо використовувати Yarn замість npm, але це не суттєво, роблять вони те ж саме. Для початку виконайте ці команди в корневому каталозі. Це встановить Webpack глобально в системі та в нашому проекті.

yarn global add webpack@2.1.0-beta.25 webpack-dev-server@2.1.0-beta.10
yarn add --dev webpack@2.1.0-beta.25 webpack-dev-server@2.1.0-beta.10

Тепер слід описати конфігурацію в файлі webpack.config.js в корневому каталозі.

'use strict';

const webpack = require("webpack");

module.exports = {
  context: __dirname + "/src",
  entry: {
    app: "./app.js",
  },
  output: {
    path: __dirname + "/dist",
    filename: "[name].bundle.js",
  },
};

Зауважте, __dirname - ім'я корневого каталогу.

Webpack розуміє що відбувається у вас в коді. Як? Він його читає (не турбуйтеся, він підписав NDA ;)). Ось алгоритм цього читання:

  1. Починаючи з context-папки...

  2. ... він шукає entry-імена файлів...

  3. ... і читає їх вміст. Кожну залежність що імпортується за допомогою import чи require() він також переглядає в пошуках вкладених залежностей. І так до самого низу, збираючи лише те, що необхідно.

  4. Записує дані в папку output.path використовуючи шаблон для імені з output.filename.

Тому, якщо ваш src/app.js виглядає так (і ви запустили yarn add --dev moment перед цим)

'use strict';
import moment from 'moment';
var rightNow = moment().format('MMMM Do YYYY, h:mm:ss a');
console.log( rightNow );
// "October 23rd 2016, 9:30:24 pm"

І ми запустимо

webpack -p

-p означає production, що включає мініфікацію і зжаття.

То отримаємо файл dist/app.bundle.js, що виводить поточний час в консоль. Зауважте, що Webpack сам знає що ви маєте на увазі, коли звертаєтесь до 'moment' (якщо в вас немає файлу moment.js)

Робота з декількома файлами

Ви можете працювати відразу з декількома файлами, модифікуючи об'єкт entry.

Декілька файлів, зібраних в один

'use strict';

const webpack = require("webpack");

module.exports = {
  context: __dirname + "/src",
  entry: {
    app: ["./home.js", "./events.js", "./vendor.js"],
  },
  output: {
    path: __dirname + "/dist",
    filename: "[name].bundle.js",
  },
};

В кінці ми отримаємо весь код в файлі dist/app.bundle.js в тому порядку, як ми вказали.

Декілька вхідних та вихідних файлів

const webpack = require("webpack");

module.exports = {
  context: __dirname + "/src",
  entry: {
    home: "./home.js",
    events: "./events.js",
    contact: "./contact.js",
  },
  output: {
    path: __dirname + "/dist",
    filename: "[name].bundle.js",
  },
};

Цей код розпакує все в три файли: dist/home.bundle.js, dist/events.bundle.js, та dist/contact.bundle.js.

Просунуте автопакування

Якщо ви розбиваєте свій додаток на декілька вихідних файлів, можливо, ви отримаєте дублікати коду в цих файлах, так як Webpack розглядає кожну залежність окремо. Але, на щастя, Webpack має вбудований плагін для цього:

module.exports = {
  // …
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: "commons",
      filename: "commons.js",
      minChunks: 2,
    }),
  ],
// …
};

Тепер в ваших вихідних файлах не буде модулів, що завантажуються 2 або більше рази (вказано в minChunks), вони будуть запаковані в common.js, який потім можна кешувати на стороні клієнта. Звісно, це ще один HTTP-запит, але це збереже користувачів від завантаження однієї бібліотеки більше одного разу.

Розробка

Webpack має власний сервер для розробки, тому для розробки статичного файлу або прототипування фронтенду він підходить прекрасно. Щоб його запустити, додайте об'єкт devServer до webpack.config.js

module.exports = {
  context: __dirname + "/src",
  entry: {
    app: "./app.js",
  },
  output: {
    filename: "[name].bundle.js",
    path: __dirname + "/dist/assets",
    publicPath: "/assets",            // New
  },
  devServer: {
    contentBase: __dirname + "/src",  // New
  },
};

Тепер створіть файл src/index.html з аким вмістом:

<script src="/assets/app.bundle.js"></script>

І запустіть сервер:

webpack-dev-server

Ваш сервер тепер працює на localhost:8080.

Webpack сам довантажить всі файли, якщо відбулися якісь зміни. Але зміни в файлі конфігурації потребують перезапуску сервера.

Глобально доступні функції

Потрібна якась функція в глобальному просторі імен? Просто встановіть output.library в конфігу:

module.exports = {
  output: {
    library: 'myClassName',
  }
};

І ваш бандл буде прикріплено до window.myClassName. Детальніше в документації.

Завантажувачі

До сих пір ми описали лише роботу з JavaScript. Це важливо, адже JS це єдина мова, яку розуміє Webpack. Але працювати ми можемо з будь-якими типами файлів, якщо переведемо їх в JavaScript. Цю функцію виконують завантажувачі.

Завантажувачем може бути препроцесор (як Sass) чи транспілятор (як Babel). В NPM вони зазвичай називаються *-loader

Babel + ES6

Якщо ви хочете писати на ES6 з використанням Babel, то слід спочатку встановити відповідні завантажувачі.

yarn add --dev babel-loader babel-core babel-preset-es2015

і вказати як їх використовувати:

module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [{
          loader: "babel-loader",
          options: { presets: ["es2015"] }
        }],
      },

      // Лоадери для інших типів файлів йдуть сюди
    ],
  },
  // …
};

Примітка для користувачів першого Webpack'у: концепт завантажувачів лишився тим самим, але було покращено синтаксис.

Тепер Webpack шукатиме файли, що збігаються з регулярним виразом /\.js$/ й завантажує його за допомогою babel. Використання регулярних виразів дозволяє краще керувати виконанням, ви не обмежені назвою чи самим розширенням файлу. Наприклад, в вас є папка /my_legacy_code/, код в якій написаний без використання ES6. Вам слід лише трохи модифікувати вираз до /^((?!my_legacy_code).)*\.js$/. Тепер воно оминатиме файли в цьому каталозі.

CSS + завантажувач стилів

Якщо вам потрібно лише завантажити CSS в проект, то зробити це можна так. Давайте уявимо, що в нас є файл index.js, ми просо імпортуємо стилі ось так:

import styles from './assets/stylesheets/application.css';

Але отримаємо помилку, яка скаже, що нам потрібен відповідний завантажувач. Згадали, що Webpack розуміє тільки JavaScript? Тепер давайте встановимо потрібні завантажувачі:

yarn add --dev css-loader style-loader

і додамо їх до конфігу:

module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
      // …
    ],
  },
};

Завантажувачі перераховуються в зворотньому порядку, тобто css-loader запуститься перед style-loader.

Ви можете помітити, що при збірці продакшн-коду це запакує ваші стилі разом з JavaScript, а style-loader запише це в <head>. Спочатку це може показатися поганою ідеєю, але це не так. По-перше ви економите HTTP-запит, і, відповідно, час. По-друге, це вбереже ваш сайт від FOUC.

Також зауважте, що Webpack з коробки піклується про всі ваші @import.

Завантаження стилів через JavaScript це круто. Це означає, що ви можете по-новому з ним взаємодіяти. Уявимо, що button.css завантажується файлом button.js. Тоді, стилі не будуть завантажуватися якщо не завантажується сам компонент.

CSS + модулі Node

Ми можемо використовувати Webpack разом з модулями Node, імпортуючи з префіксом ~.

@import "~normalize.css";

Тепер ми можемо використовувати всі можливості керування версіями від NPM. Коли ми використовуємо Webpack, краще обрати стандартний CSS-імпорт, щоб не змушувати клієнт робити декілька запитів.

CSS модулі

Можливо, ви чули про CSS-модулі, що роблять каскадні таблиці стилів моудльними. Якщо плануєте використовувати їх, то є чудовий завантажувач css-loader (yarn add --dev css-loader):

module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          "style-loader",
          { loader: "css-loader", options: { modules: true } }
        ],
      },
      // …
    ],
  },
};

Також слід відзначити, що тепер не обов'язково використовувати тильду при імпорті модулів Node (@import "normalize.css";). Можливо, у вас з'являться помилки збірки з текстом "can’t find ___". В такому випадку слід додати об'єкт resolve до вашого конфігу, щоб Webpack краще зрозумів структуру вашого проекту.

const path = require("path");
module.exports = {
  //…
  resolve: {
    modules: [path.resolve(__dirname, "src"), "node_modules"]
  },
};

Ми спочатку вказали папку з вихідними кодами, а потім з модулями Node. Тепер Webpack буде працювати трішки краще, спочатку переглядаючи наш код, а лише потім модулі.

Sass

Потрібен Sass? Нема проблем! Встановіть:

yarn add --dev sass-loader node-sass

І додайте ще одне правило:

module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.(sass|scss)$/,
        use: [
          "style-loader",
          "css-loader",
          "sass-loader",
        ]
      } 
      // …
    ],
  },
};

Тепер, коли ваш JavaScript-файл імпортує файл .sass (або .scss), буде використано цей набір завантажувачів.

Пакування CSS окремо

Вам потрібно пакувати CSS-файли окремо? Не проблема. Це можна організувати без зміни коду, лише заміною нашого завантажувальника на плагін extract-text-webpack-plugin. Уявіть, що вас є файл app.js з цим:

import styles from './assets/stylesheets/application.css';

Тепер слід встановити плагін:

yarn add --dev extract-text-webpack-plugin@2.0.0-beta.4

і відредагувати конфіг:

const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.css$/,
        loader:  ExtractTextPlugin.extract({
          loader: 'css-loader?importLoaders=1',
        }),
      },

      // …
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: "[name].bundle.css",
      allChunks: true,
    }),
  ],
};

Тепер після запуску webpack -p ви отримаєте файл app.bundle.css, який зможете підключити до свого html, використовуючи тег <link>.

HTML

Як ви могли здогадатися, існує плагін html-loader. Для цього можуть бути різні приводи: використання JSX, Mustache чи Handlebars з такими великими системами як React, Angular, Vue чи Ember, або використання препроцесорів як Pug (Jade) чи Haml, або просто копіювання HTML з початкової папки в кінцеву.

Але тут уже слід вирішувати тобі, як це робити. Ні я, ні Webpack не розбирається в твоїй архітектурі краще тебе.

Думайте модулями

Щоб вижати з Webpack максимум, ви повинні думати модулями — маленькими, самодостатніми речами, що роблять одну дію, але добре. Тому такий код:

└── js/
    └── application.js   // 300KB спагеті-коду

повинен бути відрефакторений в ось це:

└── js/
    ├── components/
    │   ├── button.js
    │   ├── calendar.js
    │   ├── comment.js
    │   ├── modal.js
    │   ├── tab.js
    │   ├── timer.js
    │   ├── video.js
    │   └── wysiwyg.js
    │
    └── application.js  // ~ 1KB коду; імпорти з ./components/

В результаті отримуємо чистий, здатний до повторного використання код. Кожен компонент базується на власних залежностях і експортує те, що хоче зробити публічним. Приправте це все Babel+ES6 і ви отримаєте прекрасну систему модулів, яка дійсно працює.

Що почитати

Джерело перекладу

1221 6

Схожі матеріали:

Коментарі:

Авторизуйтесь, щоб залишити коментар.