Webpack 2 Release


Предупреждение: проверьте, как установлена версия Webpack в package.json. Возможно скоро ваш continuous integration сломается.


Наконец-то Webpack 2 преодолел рубеж beta и release candidate. Это значит, что всё работает стабильно, и можно без опаски использовать его в production.


Список изменений/улучшений:


  • Нативная поддержка ES6-модулей;
  • Разбиение кода на чанки — Code Splitting with ES6;
  • Поддержка динамических выражений при загрузке модулей — Dynamic expressions;
  • Изменения при работе с Babel;
  • ES6-специфичные оптимизации;
  • Breaking Changes;
  • Новый сайт с документацией.

Подробности под катом.


Нативная поддержка ES6-модулей


Теперь Webpack для работы с модулями поддерживает конструкции с использованием ключевых слов import и export без предварительной трансформации кода в CommonJS, и делает это весьма хорошо. Чтобы убедиться в гибкости поддержки, давайте взглянем на следующий код:


import { currentPage, readPage } from "./book";

currentPage === 0; //true
readPage();
currentPage === 1; //true

// book.js
export var currentPage = 0;

export function readPage() {
    currentPage++;
}

export default "This is a book";

Разбиение кода на чанки — Code Splitting with ES6


Использование System.import предоставляет возможность отложенной загрузки модулей в рантайме.


Если какой-либо модуль загружается с помощью System.import, Webpack отделит его от остального кода в независимый chunk.


System.import принимает имя модуля/путь к модулю и возвращает Promise, так что его использование отлично сочетается с async/await;


function onClick() {
    System.import("./module").then(module => {
        module.default;
    }).catch(err => {
        console.log("Chunk loading failed");
    });
}

UPDATE:


System.import will be deprecated in webpack 2 release (removed in webpack 3) as it's behavior is incorrect according to the spec
import() должен быть использован вместо System.import()

Поддержка динамических выражений при загрузке модулей — Dynamic expressions


Если использовать System.import с динамическим выражением в качестве пути к модулю, то Webpack упакует код, каждого из модулей, соответствующих паттерну, в отдельный chunk.


function route(path, query) {
    return System.import("./routes/" + path + "/route")
        .then(route => new route.Route(query));
}
// This creates a separate chunk for each possible route

Изменения при работе с Babel


Привычный всем babel-preset es2015, по-умолчанию, трансформирует ES6 модули в CommonJS. Вместо него теперь нужно использовать babel-preset es2015-webpack.


UPDATE:
babel-preset es2015-webpack объявлен deprecated вместо него нужно использовать опции:


{
    "presets": [
        [ "es2015", { "modules": false } ]
    ]
}

ES6-специфичные оптимизации


Статический характер ES6 модулей открывает возможности для новых оптимизаций на этапе компиляции. Например, теперь Webpack сможет находить неиспользуемые export-ы и удалять их — Tree-shaking.


Так же заявлена поддержка ES6 export mangling — минификация имени экспортируемой переменной или функции, если это возможно.


Изменения в конфигурационном файле


Раньше во многих Webpack-конфигах для настройки опциональных параметров использовались переменные окружения. Webpack 2 превносит еще один способ их определения извне.


Теперь при запуске Webpack-а через командную строку можно указать один или несколько аргументов --env в следующем формате: (--env dev => "dev") или (--env.minimize --env.server localhost => {minimize: true, server: "localhost"}). Рекомендуется использовать второй вариант.


// webpack.config.babel.js
exports default function(options) {
    return {
        // ...
        devtool: options.dev ? "cheap-module-eval-source-map" : "hidden-source-map"
    };
}

Много улучшений в подсистеме Webpack-а, отвечающей за resolving. Она стала гибче, а вероятность настроить что-то неправильно снизилась.


Новые опции:


{
    modules: [path.resolve(__dirname, "app"), "node_modules"]
    // (was split into `root`, `modulesDirectories` and `fallback` in the old options)
    // In which folders the resolver look for modules
    // relative paths are looked up in every parent folder (like node_modules)
    // absolute paths are looked up directly
    // the order is respected

    descriptionFiles: ["package.json", "bower.json"],
    // These JSON files are read in directories

    mainFields: ["main", "browser"],
    // These fields in the description files are looked up when trying to resolve the package directory

    mainFiles: ["index"]
    // These files are tried when trying to resolve a directory

    aliasFields: ["browser"],
    // These fields in the description files offer aliasing in this package
    // The content of these fields is an object where requests to a key are mapped to the corresponding value

    extensions: [".js", ".json"],
    // These extensions are tried when resolving a file

    enforceExtension: false,
    // If false it will also try to use no extension from above

    moduleExtensions: ["-loader"],
    // These extensions are tried when resolving a module

    enforceModuleExtension: false,
    // If false it's also try to use no module extension from above

    alias: {
        jquery: path.resolve(__dirname, "vendor/jquery-2.0.0.js")
    }
    // These aliasing is used when trying to resolve a module
}

Breaking changes


Polyfills


Динамическая загрузка чанков теперь будет работать только, если Promise доступен. Так что если необходима поддержка старых браузеров, то обеспечивать поддержку Promise через полифилы нужно будет самому разработчику web-приложения.


Сделано это для того, чтобы избежать дублирования кода Promise-полифилов в разных частях bundle-js.


Can I use Promises?


Так же для старых браузеров через полифилы необходимо обеспечть поддержку следующих функций:


Object.defineProperty,
Function.prototype.bind,
Object.keys


Loaders configuration


Webpack-loaders теперь конфигурируются с помощью параметра resourcePath вместо resource. Это означает, что query string больше не участвуют regex-matching.


Теперь вместо сложных конструкций вроде /\.svg($|\?)/ достаточно просто /\.svg$/.


Синтаксис использования Webpack-loaders выглядит следующим образом:


loaders: [
    {
        test: /\.css$/,
        loaders: [
            "style-loader",
            { loader: "css-loader", query: { modules: true } },
            {
                loader: "sass-loader",
                query: {
                    includePaths: [
                        path.resolve(__dirname, "some-folder")
                    ]
                }
            }
        ]
    }
]

HMR communication


Webpack 1 для уведомлений о пересборке бандла использовал Web Messaging API (postMessage). Webpack 2 использует стандартный event emitter. Это означает, что обвязка для WebSocket будет включена в бандл.


webpack-dev-server теперь по-умолчанию использует режим "inlined".


Это должно позволить использовать webpack-dev-server для горячей замены кода в Web Workers.


Code Splitting


require.ensure и AMD require теперь всегда работают асинхронно, даже если чанк уже загружен.


Ссылки:


  1. Webpack 2.2: The Final Release
  2. Новый сайт с докментацией
  3. Migrating from v1 to v2

Картинка из официального блога Webpack на Medium

Поделиться с друзьями
-->

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


  1. BoryaMogila
    19.01.2017 09:18
    +9

    Спасибо за статью.


  1. devpreview
    19.01.2017 10:53
    +4

    Два года ждал.
    (извините, не удержался)


    1. Markus_Kane
      19.01.2017 11:29
      +1

      Обещанного три года ждут :)


    1. zekohina
      19.01.2017 12:09
      +15

      Джва года


  1. kernel72
    19.01.2017 11:29
    +1

    Ура!


  1. mihap
    19.01.2017 11:35
    +2

    System.import will be deprecated in webpack 2 release (removed in webpack 3) as it's behavior is incorrect according to the spec


    import() должен быть использован вместо System.import


    1. MrCheater
      19.01.2017 11:37

      Спасибо за ценное замечание


  1. pribilinskiy
    19.01.2017 11:42
    +1

    Забыли упомянуть что на данный момент он все еще инсталлится с тегой beta

    npm install webpack@beta
    

    Но через неделю обещают перевести в latest


  1. mihap
    19.01.2017 12:16
    +1

    https://github.com/mihap/webpack-react-hot-bolerplate

    Проапдейтил свой бойлерплейт. Кроме webpack v2.2.0 там еще react-router v4-alpha.6 и react-hot-loader v3-beta.6
    Можно использовать и для продакшн, с верой в удачу и оптимизмом =), но скорее как пример конфига для апгрейда.



    1. reactoranime
      19.01.2017 13:11

      Интересно, есть где-нибудь рабочие сборки с babel-polyfill для IE11? А еще лучше разбитые на части vendor/bundle chunks, с использованием CommonChunksPlugin. Сколько не пробовал, выглядит как костыль, и толком не работает. Есть кто сталкивался с этим?


  1. k12th
    19.01.2017 12:28

    По-моему, самое ценное изменение — появление человекочитаемой документации:) И даже конфиг стал чуть понятнее (хотя еще есть куда расти).


    1. raveclassic
      19.01.2017 12:44
      +1

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


  1. Defari
    19.01.2017 13:01
    +3

    В настройках лоадера, query: {} не является валидным параметром. Нужно использовать options: {}.


  1. Andrey7287
    20.01.2017 11:34

    Исправлен ли баг с background images


  1. lookman
    25.01.2017 15:41

    Хорошая статья.
    Спасибо за четко изложенное и понятное описание изменений!


  1. Vahan
    25.01.2017 15:41

    Одна проблема: uglify-js пока что не перешел на ES6, так что приходиться использовать harmony бренч.

    "uglify-js": "git://github.com/mishoo/UglifyJS2#harmony",
    


    1. reactoranime
      25.01.2017 17:06

      а как же быть с плагином для webpack:


          new webpack.optimize.UglifyJsPlugin({
            compress: {
              // warnings: false,
              // drop_console: true
            },
            output: {
              comments: false
            }
          })


      1. Vahan
        25.01.2017 17:30

        О чем вы? Это он и есть ( https://github.com/webpack/webpack/blob/v2.2.0/package.json ), добовляем в dependencies ту строку, чтоб взял с другого бренча.