[Перевод] Пособие по webpack

7076951662814365a0bfff3b1f3e83de.png


Давайте сначала разберемся, зачем нужен вебпак (webpack), и какие проблемы он пытается решить, а потом научимся работать с ним. Webpack позволяет избавиться от bower и gulp/grunt в приложении, и заменить их одним инструментом. Вместо bower’а для установки и управления клиентскими зависимостями, можно использовать стандартный Node Package Manager (npm) для установки и управления всеми фронтэнд-зависимостями. Вебпак также может выполнять большинство задач grunt/gulp’а.


Bower это пакетный менеджер для клиентской части. Его можно использовать для поиска, установки, удаления компонентов на JavaScript, HTML и CSS. GruntJS это JavaScript-утилита командной строки, помогающая разработчикам автоматизировать повторяющиеся задачи. Можно считать его JavaScript-альтернативой Make или Ant. Он занимается задачами вроде минификации, компиляции, юнит-тестирования, линтинга и пр.

Допустим, мы пишем простую страницу профиля пользователя в веб-приложении. Там используется jQuery и библиотеки underscore. Один из способов — включить оба файла в HTML:




  
    
    User Profile
    
    
  
  
    

Это простой HTML с Бутстрапом. Мы подключили jQuery и underscore с помощью тега script.


Давайте рассмотрим файл profile.js, который использует подключенные нами библиотеки. Наш код лежит внутри анонимного замыкания, которое хранит бизнес-логику приложения. Если не замыкать код в функцию, то переменные будут находиться в глобальном окружении, и это плохо.


(function(){
  var user = {
    name : "Shekhar Gulati",
    messages : [
      "hello",
      "bye",
      "good night"
    ]
  };

  $("#timeline").text(user.name+ " Timeline");

  _.each(user.messages, function(msg){
    var html = "
  • "+msg+"

  • "; $(".timeline").append(html); }); }());

    Код будет исполнен при вызове скрипта. Если открыть страницу в браузере, то профиль будет выглядеть так.


    fd0ad302271c4d79a1d994efd809985e.png


    У этого кода две задачи:


    1. получить информацию о пользователе
    2. настроить таймлайн.

    Известно, что смешивать понятия — плохая практика, так что нужно написать отдельные модули, отвечающие за определенные задачи. В файле profile.js мы использовали анонимное замыкание для хранения всего кода. В JavaScript есть способы делать модули получше. Два популярных способа это CommonJS и AMD.


    • Модуль CommonJS это грубо говоря кусок повторно используемого кода, который экспортирует определенные объекты, и они становятся доступными другим модулям с помощью require.
    • Asynchronous Module Definition (AMD) позволяет загружать модули асинхронно.

    Если хотите узнать о модулях в JavaScript больше, то советую прочитать статью JavaScript Modules: A Beginner«s Guide.


    А в этой статье мы будем писать модули на CommonJS. Давайте напишем модуль timeline с методами для установки хедера и таймлайна. В CommonJS можно импортировать зависимости с помощью функции require. Таймлайн зависит от jquery и underscore.


    var $ = require('jquery');
    var _ = require('underscore');
    
    function timeline(user){
      this.setHeader = function(){
          $("#timeline").text(user.name+ " Timeline");
      }
    
      this.setTimeline = function(){
        _.each(user.messages, function(msg){
          var html = "
  • "+msg+"

  • "; $(".timeline").append(html); }); } } module.exports = timeline;

    Этот код создает новый модуль timeline. Есть две функции: setHeader и setTimeline. Мы используем специальный объект module и добавляем ссылку на нее в module.exports. Таким образом мы сообщаем модульной системе CommonJS, что хотим позволить другим функциям использовать модуль.


    Теперь обновим profile.js, он должен использовать модуль timeline. Можно создать новый модуль, который будет загружать информацию о пользователе, но пока давайте ограничимся одним модулем.


    var timeline = require('./timeline.js');
    var user = {
      name : "Shekhar Gulati",
      messages : [
        "hello",
        "bye",
        "good night"
      ]
    };
    
    var timelineModule = new timeline(user);
    timelineModule.setHeader(user);
    timelineModule.setTimeline(user);

    Если загрузить index.html в браузере, то отобразится пустая страница. В консоли (в developer tools) можно обнаружить ошибку:


    profile.js:1 Uncaught ReferenceError: require is not defined

    Проблема в том, что браузеры не понимают модульную систему вроде CommonJS. Нужно предоставить браузеру формат, который он ожидает.


    Бандлеры модулей идут на помощь

    Веб-браузеры не понимают эти хорошо описанные модули. Нужно или добавить весь JavaScript-код в один файл и импортировать его, или нужно добавить все файлы вручную на страницу с помощью тега script. Используем бандлер модулей (module bundler) для решения этой проблемы. Бандлер модулей комбинируют разные модули и их зависимости в один файл в правильном порядке. Он может парсить код, написанный с использованием разных модульных систем, и комбинировать в один формат, понятный браузеру. Два популярных бандлера модулей это:


    1. browserify: пакует npm-модули, чтобы потом использовать их в браузере. В случае с browserify приходится дополнительно подключать Grunt или Gulp для линтинга, запуска тестов и пр. Это значит, что нужно тратить время на работу с несколькими инструментами и интеграцией.
    2. webpack: система сборки, которая предоставляет не только бандлинг (компоновку) модулей, но и может выполнять задачи, которыми занимаются Gulp/Grunt. К тому же, вебпак не ограничивается JavsScript-файлами, он может работать с другой статикой вроде CSS, картинок, html-компонентов и др. Вебпак также поддерживает очень полезную фичу — code splitting (разбиение кода). Большое приложение можно разбить на куски, которые загружаются по мере необходимости.

    Что такое вебпак?

    Официальное определение звучит так:


    webpack берет модули с зависимостями и генерирует статические ресурсы, которые представляют эти модули

    Это определение теперь имеет смысл, когда понятна решаемая проблема. Вебпак берет набор ресурсов и трансформирует их в наборы (бандлы).


    bfef0f61c7864ffca339039b07100a79.png


    Трансформацией ресурсов занимаются загрузчики, которые являются сердцем вебпака.


    Вебпак в действии

    Для установки вебпака нужен node. Можно скачать node с официального сайта.


    Теперь можно установить вебпак глобально:


    $ npm install -g webpack

    Создайте новый модуль командой npm init. Она создаст файл package.json. Установите зависимости с помощью npm.


    $ npm install -S jquery
    $ npm install -S underscore

    В дополнение, нужно установить вебпак как зависимость.


    $ npm install -S webpack

    Замените index.html следующим кодом. Как видите, мы удалили все теги script для jquery и underscore. Также, вместо импорта js/profile.js импортируется dist/bundle.js.


    
    
      
        
        User Profile
        
        
    
      
      
    
        

    Давайте создадим бандл.


    $ webpack js/profile.js dist/bundle.js

    Hash: 6d83c7db8ae0939be3d0
    Version: webpack 1.13.2
    Time: 350ms
        Asset    Size  Chunks             Chunk Names
    bundle.js  329 kB       0  [emitted]  main
       [0] ./js/profile.js 252 bytes {0} [built]
       [1] ./js/timeline.js 427 bytes {0} [built]
        + 2 hidden modules

    Теперь страница работает нормально.


    Можно сделать так, чтобы вебпак следил за изменениями и генерировал бандл автоматически. Для этого нужно запустить его с таким флагом:


    $ webpack -w js/profile.js dist/bundle.js

    Теперь вебпак не будет завершаться сам. При изменении файлов будет генерироваться новый бандл. Нужно лишь перезагрузить страницу в браузере. Давайте изменим profile.js:


    var user = {
      name : "Shekhar Gulati!!!",
      messages : [
        "hello",
        "bye",
        "good night"
      ]
    };

    Файл bundle.js, сгенерированный вебпаком, содержит много кода, относящегося к самому вебпаку, а ваш код там будет в измененном виде. Будет очень неудобно отлаживать приложение в браузере, в инструментах разработчика, например. Чтобы упростить себе жизнь, можно запустить вебпак с флагом devtools.


    $ webpack -w --devtool source-map js/profile.js dist/bundle.js

    Вебпак сгенерирует source map для файла bundle.js. Source map связывает минимизированный и собранный в один файл код с исходным, несобранным кодом. Для тестирования можно добавить строчку debugger в profile.js


    var timeline = require('./timeline.js');
    var user = {
      name : "Shekhar Gulati",
      messages : [
        "hello",
        "bye",
        "good night"
      ]
    };
    
    debugger;
    var timelineModule = new timeline(user);
    timelineModule.setHeader(user);
    timelineModule.setTimeline(user);

    Перезагрузите страницу, и приложение остановится на этой строке.


    d09f2ab756d148b0af1fc5b76795b998.png


    Добавление CSS


    В HTML выше видно, что мы загружаем /css/style.css. Вебпак умеет работать не только с JavaScript, но и с другой статикой, в том числе CSS. Удалите строку с /css/style.css из index.html. Мы будем подключать стили в profile.js таким образом:


    require('../css/style.css');
    
    var timeline = require('./timeline.js');
    var user = {
      name : "Shekhar Gulati",
      messages : [
        "hello",
        "bye",
        "good night"
      ]
    };
    
    var timelineModule = new timeline(user);
    timelineModule.setHeader(user);
    timelineModule.setTimeline(user);

    вебпак перезагрузит изменения, и мы увидим сообщение об ошибке в консоли:


    ERROR in ./css/style.css
    Module parse failed: /Users/shekhargulati/dev/52-technologies-in-2016/36-webpack/code/css/style.css Unexpected token (1:0)
    You may need an appropriate loader to handle this file type.

    Проблема в том, что вебпак не понимает CSS по умолчанию. Нужно установить пару загрузчиков для этого. Вот команда для установки необходимых загрузчиков:


    $ npm install style-loader css-loader --save-dev

    Вебпак использует загрузчики для трансформации текста в нужный формат. Теперь нужно обновить require:


    require('style!css!../css/style.css');

    Синтаксис style!css! означает, что сначала нужно применить трансформацию css для конвертации текста из style.css в CSS, а потом применить стиль к странице с помощью трансформации style.


    Запустите вебпак снова.


    $ webpack -w --devtool source-map js/profile.js dist/bundle.js

    Конфигурация

    Чтобы не указывать все опции в командной строке, можно создать конфигурационный файл webpack.config.js в корне приложения:


    module.exports = {
      context: __dirname,
      devtool: "source-map",
      entry: "./js/profile.js",
      output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
      }
    }

    Теперь можно запускать вебпак простой командой webpack -w.


    Когда мы добавили style!css! в profile.js, мы смешали продакшен-код с конфигурацией вебпака. Можно перенести эту опцию в файл конфигурации.


    После изменений конфигурации нужно перезапускать вебпак.


    var webpack = require('webpack');
    
    module.exports = {
      context: __dirname,
      devtool: "source-map",
      entry: "./js/profile.js",
      output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
      },
      module:{
        loaders: [
          {test : /\.css$/, loader: 'style!css!'}
        ]
      }
    }

    Самая интересная секция тут это декларация модулей. Тут мы указали, что если файл заканчивается на .css, то нужно применять трансформацию style!css!.


    Горячая перезагрузка


    Для горячей перезагрузки (hot reloading) нужен webpack-dev-server. Установите его так:


    $ npm install -g webpack-dev-server

    Теперь можно запускать сервер командой webpack-dev-server.


    Мы запустим сервер по адресу http://localhost:8080/webpack-dev-server/ с конфигурацией из webpack.config.js.


    Порт можно изменить опцией --port.


    $ webpack-dev-server --port 10000

     http://localhost:10000/webpack-dev-server

    Конфигурацию webpack-dev-server также можно указать в файле webpack.config.js, в секции devServer.


    module.exports = {
      context: __dirname,
      devtool: "source-map",
      entry: "./js/profile.js",
      output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
      },
      module:{
        loaders: [
          {test : /\.css$/, loader: 'style!css!'}
        ]
      },
      devServer: {
        inline:true,
        port: 10000
      },
    }


    На сегодня все. Узнать больше о вебпаке можно из документации.

    Комментарии (0)

    © Habrahabr.ru