Привет, Хабр! Сегодня я расскажу вам о Webpack 4 с разделением кода на отдельные модули, а также о интересных решениях, которые помогут вам быстрее собрать сборку на webpack 4. В конце, я предоставлю свою базовую сборку на webpack c самыми необходимыми инструментами, которую вы в последствие сможете расширить. Данная сборка вам поможет понять данный материал, а также возможно поможет быстрее написать свою реализацию и быстрее решить возможные проблемы.

Основная идеология данной сборки — это корректное разделения кода внутри конфигурационного файла для удобства использования, чтения и чистоты webpack.config.js. Необходимые модули и плагины для dev и prod версии(а также для разделения функционала в главном файле) будут находиться в отдельной папке webpack и из неё импортироваться для соединения с главным конфигом.

Зачем нужен такой подход?


С постепенным ростом количества модулей, плагинов и т.д, которыми обрастает ваша сборка на вебпаке, конфигурационный файл растёт как на дрожжах. Со временем данный файл становится трудно читаемым, а изменять его под конкретный проект, если какие-то модули в нём не используется становится труднее, а хочется чего-то универсального. Поэтому требуется чёткая организация кода.

Что нам понадобится?


Мы будем использоваться плагин webpack-merge.

Создаём папку webpack и выносим все отдельные модули в отдельные файлы. Например, webpack/pug.js, webpack/scss.js и экспортируем из них эти функции.

Файл pug.js

module.exports = function() {
  return {
    module: {
      rules: [
        {
          test: /\.pug$/,
          loader: 'pug-loader',
          options: {
            pretty: true,
          },
        },
      ],
    },
  };
};

Файл webpack.config.js. В данном файле мы их подключаем, и с помощью данного плагина удобно и быстро соединяем.

const merge = require('webpack-merge');
const pug = require('./webpack/pug');

const common = merge([
  {
    entry: {
      'index': PATHS.source + '/pages/index/index.js',
      'blog': PATHS.source + '/pages/blog/blog.js',
    },
    output: {
      path: PATHS.build,
      filename: './js/[name].js',
    },
    plugins: […],
    optimization: { … },
    
  },
  pug(),
]);

Теперь если у нас есть новая задача, под которую нужен новый модуль, плагин, лоадер, то мы выносим его в отельный модуль(файл) и помещаем в папку webpack, а затем подключаем его в главный конфигурационный файл, сохраняя конфиг максимально чистым.

Настройки для production и development


Сейчас мы с помощью банального if закончим наше разделение, к которому мы стремились, и настроим наш вебпак под эти два типа разработки, благодаря чему станет окончательно удобно пользоваться данным инструментом, а так же в будущем сможем гибко и просто настраивать его под любой другой проект, или же расширять в текущем. Для экспорта в ноду(для самой работы вебпака) в webpack 4 мы используем следующую конструкцию:

module.exports = function(env, argv) {
  if (argv.mode === 'production') {
    return merge([
      common,
      extractCSS(),
      favicon(),
    ]);
  }
  if (argv.mode === 'development') {
    return merge([
      common,
      devserver(),
      sass(),
      css(),
      sourceMap(),
    ]);
  }

В объект common мы подключаем то, что используется и на проде и в разработке, а в условиях подключаем только те модули, которые необходимы в этих случаях.

Теперь хотелось бы поговорить об основных особенностях webpack 4 относительно webpack 3


  • Для быстрого запуска, webpack 4 не нуждается в webpack.config.js, ему теперь необходима лишь точка входа (index.js)
  • В новой версии webpack command line interface вынесен в отдельный пакет и нужно установить webpack-cli.
  • Для запуска webpack 4, нужно (иначе будет warning) в скриптах указать mode (режим работы) --mode production или --mode development, в зависимости от ключа меняется работа вебпака. Режим development направлен для ускорения сборки. В production варианте направлено всё на итоговую минификацию кода.
  • Для того что бы создать common.js и common.css файлы, более не используется CommonsChunkPlugin, за это теперь отвечают splitChunks и используется следующая конструкция:

        optimization: {
          splitChunks: {
            cacheGroups: {
              'common': {
                minChunks: 2,
                chunks: 'all',
                name: 'common',
                priority: 10,
                enforce: true,
              },
            },
          },
        },
    

    В вебпак 3 – это было бы так в plugins:

    new webpack.optimize.CommonsChunkPlugin({ name: 'common ', })

    Соответственно в чанках в HtmlWebpackPlugin подключаем (тут без изменений).

        
    plugins: [
          new HtmlWebpackPlugin({
            filename: 'index.html',
            chunks: ['index', 'common'],
            template: PATHS.source + '/pages/index/index.pug',
          }),
        ],
    

  • Следующий важный момент, для того чтобы создать sourceMap, теперь мы используем следующий подход — создаём файл sourceMap.js в папке webpack и подключаем в дев версии например (как указано выше):

    module.exports = function() {
      return {
        devtool: 'eval-sourcemap',
      }; 
    };
    

Теперь подход с plugins: [new webpack.optimize.UglifyJsPlugin({}) ] не работает.

На этом я хотел бы завершить свой рассказ и предоставить свою базовую сборку — ссылка на webpack 4, которая возможно вам поможет в работе и освоение. Спасибо за внимание!

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


  1. justboris
    21.10.2018 21:21
    +1

    Хорошая идея про модульное оформление, гораздо лучше читается, но зачем вы используете второй аргумент argv для определения окружения? Официальная документация рекомендует использовать параметр env.


    1. Anisov Автор
      21.10.2018 22:14

      Модуль exports(module.exports = function(env, argv) ) может быть функцией, которая принимает env(environment) и argv, если сделать console.log(argv) внутри этой функции, и вызвать npm run build вы увидите кучу параметров(опций), в этом js объекте есть знакомый ключ — mode cо значением 'production'. А взялось это из скрипта(файл package.json), мы указывали, что при npm run build нужно использовать -> mode production, при start будет mode development.


      1. justboris
        22.10.2018 01:50

        Я понимаю, что и так тоже будет работать, но я не понимаю, зачем делать по-другому, когда в документации есть решение.


  1. CuamckuyKot
    22.10.2018 01:06
    +1

    Обратите внимание на пути:


    PATHS.source + '/pages/index/index.pug'

    Под Windows такого вида unix-пути не заработают.


    Правильнее писать так:


    const path = require('path');
    const merge = require('webpack-merge');
    const pug = require('./webpack/pug');
    
    const common = merge([
      {
        entry: {
          'index': path.join(PATHS.source,'pages','index','index.pug'),
          'blog': path.join(PATHS.source,'pages','blog','blog.js'),
        },
        output: {
          path: PATHS.build,
          filename: path.join('.','js','[name].js'),
        },
        plugins: […],
        optimization: { … },
    
      },
      pug(),
    ]);


    1. Anisov Автор
      22.10.2018 01:50

      Данная сборка была разработана под ОС Windows 10, так как большинство времени во front-end разработке проводится именно под этой ОС. В back-end разработке конечно же Linux используется мною как основная и главная ОС, но в данном случает под виндой всё работает идеально. А так замечание справедливое, можно сделать и так, если хочется, но у меня такой надобности не было.


      1. Skerrigan
        22.10.2018 05:09

        Ну… если уж выкладывать в «public access» что-либо, то все же стоит задаться с самого начала вопросами «привести в порядок». А в это уже входит не «мне было удобно — сделал так, чисто для себя», а «делать как надо». Вы же для людей, а не для себя таки?
        P.S. Забавно, ровно то же самое, что и в топике, пилил для своей фирмы еще летом — тогда, по иронии, материала было «кот наплакал» и мигрировать v2->v4 было больно.

        UPD:

        У вас там 'ошибки' в конфиге есть.
        ProvidePlugin на сегодняшний день устарел и не обеспечивает глобальной доступности для jQ. Если вам все же она необходима, тогда использовать нужно такой вариант:
        test: require.resolve('jquery'),
                        use: [{
                            loader: 'expose-loader',
                            options: 'jQuery'
                        },{
                            loader: 'expose-loader',
                            options: '$'
                        }]

        А в packaje.json прописать что-то такое
        «expose-loader»: "^0.7.5",


        И вот уже теперь можно будет в консольке написать просто знак $, и jQ нормально будет доступен.
        Вы можете спросить «а зачем это надо?», на что сразу отвечу — когда вы подключаете какой-либо плагин jQ вне сборщика, то он не будет иметь доступа до библиотеки при использовании ProvidePlugin.

        UPD2: Да и hot-reload неплохо было бы использовать (флаг -w в npm-конфиге)… еще стоило бы прикрутить кеширование (хотя бы в один уровень) — скорость сборки бандлов можно снизить в несколько раз.


        1. Anisov Автор
          22.10.2018 09:01

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


          1. Skerrigan
            22.10.2018 09:29

            не посчитал нужным использовать тут

            он приложен как хороший

            не для себя, а для общего пользования

            Это взаимо-противоречие.
            Если вы делаете обучающий материал, то он не должен в себе содержать неправильного/ошибок.
            А у вас они есть! Вы исходно предоставляете не правильный вариант — в этом смысл. И вам делают подсказки люди, у которых больше знаний/опыта. Зачем упорно гнуть свою линию (что материал не «об этом»)?
            Лучше бы внесли исправления в репозиторий — это ведь по сути универсальные аспекты, без разницы «какой там проект» (иначе вообще тот же jQ и в вашем материале лишний — у людей его может и не быть,… а раз уж вы его подключили, то подключайте верным образом) :)
            Ровно то же самое касается и «путей» — без разницы, что у вас Win10 (у меня так же, к слову). Важно писать универсальный вариант, с «правильными» путями.
            P.S. Да и в целом — «вселенная node.js» не мой дом родной, я из Java-world, тем не менее… :)


            1. faiwer
              22.10.2018 10:06
              +1

              Скажите, что неправильного в игнорировании path.join в пользу обычной конкатенации unix-like. Насколько я понимаю, проблемы могут возникнуть:


              • при вызове spawn, exec и пр. внешние запуски других приложений
              • хитрые махинации с частями путей, полученными из разных источников

              Я ничего не упустил? Суть именно во втором пункте? Что-то вроде складывания C:\somepath и otherpath/file.txt?


              P.S. вообще не использую path.join (каюсь, грешен), т.к. давным давно не видел Windows на машинах разработчиков, и сам не использую. Хочу понять насколько проблема реально актуальна.


              1. Skerrigan
                22.10.2018 10:36

                Я ничего не упустил? Суть именно во втором пункте? Что-то вроде складывания C:\somepath и otherpath/file.txt?

                Вот конкретно подобное у нас таки «сплошь и рядом» (в основном из-за того, что разные задачи требуют разные суб-инструменты, где бывают предпочтительными разные виды слешей,… ага, как раз тоже «не заморачивались универсальностью»).
                За остальное не скажу, лишь, если говорить конкретно о путях, то, что когда сам писал с большей платформо-зависимостью, мне сразу «по рукам стучали» (ибо нефиг привыкать писать «тяп-ляп», лучше один раз написать «как надо» и чтобы потом работало N-лет всегда, надежным образом — и так существует множество ситуаций, когда приходится намеренно писать «плохой код», так зачем это делать еще и, когда можно без этого?).
                *у нас судя по всему практически все (за исключением пары машин (одна из которых сервак) — все на Win). У знакомых, в фирмах размера ~>100ч., аналогично — всюду Win. При том, что мизерный процент чего-либо таки на Unix.
                оффтоп
                P.S. Но исходно речь была немного об ином: что поправки к статье автора ничем не вредят его материалу,… более того, делают материал более верным и полезным. Та же быстрая компиляция в бандл — я не могу назвать человека, которому пригодится именно медленная сборка (а не быстрая).
                Но автор считает это «не нужным», не смотря на
                интересных решениях, которые помогут вам быстрее собрать сборку

                При этом материал нужен как раз новичкам в мире node/npm/webpack — тем самым людям, которые еще не умеют отличать хорошего от плохого.