Если вы разрабатываете на современном javascript, то почти любой ваш модуль содержит простыню таких строк:

import React from 'react'
import $ from 'jquery'
...

Как оказалось, большинство этих строк можно не писать, доверив их генерацию автоматике. И помогает в этом новомодный webpack, в котором, как оказывается, полно приятных сюрпризов. Кроме всем известных require и import для любых файлов и уже описанного на хабре «hot module replacement», webpack может проанализировать ваш исходный код и автоматически включить нужные модули на основании используемых литералов. Под катом — краткое описание как работает эта магия.

За анализ ваших исходников и автоматическое создание import директив отвечает специальный плагин ProviderPlugin, который встроен в webpack и не требует установки. Чтобы магия сработала, необходимо указать плагин в конфигурационном файле wbpack и снабдить его списком идентификаторов и модулей. Как известно, webpack использует парсер esprima, и поэтому имеет весьмы точное представление о структуре вашего кода. Встретив в исходнике указанный индентификатор, webpack сгенерирует код загрузки указанного модуля так же, как это он это делает для import или require. Фрагмент конфигурационного файла:

module.exports = {
  plugins: [
    new webpack.ProvidePlugin({
      'React':     'react',
...

При использовании плагина с конфигурацией из примера, webpack будет искать использование индентификатора React. Он проигнорирует такую строку:

const foo = "React";

и даже такую:

bar.React = true;

Зато встретив вот такую, сразу поймет что в этом модуле используется ReactJS и снабдит фрагмент bundle кодом загрузки соответствующего модуля:

React.createClass(…)

И конечно же никто не ограничивает вас только ReactJS. Используйте этот способ для популярных библиотек и часто использумых модулей вашего собственного проекта, чтобы сделать код лакониченее и проще:

new webpack.ProvidePlugin({
    '$':          'jquery',
    '_':          'lodash',
    'ReactDOM':   'react-dom',
    'cssModule':  'react-css-modules',
    'Promise':    'bluebird'
  })

P.S.


Если вы, как и я, хотите использовать ES6 import вместо старенького require, то делается это путем указания babel как loader'а для webpack. И не забывайте про .babelrc и presets — в последней версии babel разработчики подготовили сюрприз для новичков, без указания presets babel теперь не делает ничего:

module.exports = {
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
...

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


  1. vintage
    31.12.2015 14:48

    А у меня свой велосипед с автолоадером. Достаточно написать:
    var row = new $my_app_row
    Или, например:
    var row = $my.app.row.make()

    И из директории /my/app/row/ будут подключены все файлы (скрипты, стили, шаблоны и тп), что очень удобно и не требует никаких специальных конфигов.


    1. Aetet
      31.12.2015 15:04
      +6

      Угу. Наелся я таких бесконфигурационных систем с БЭМом, и каким-то фреймворком созвучным с tonic, который по всему коду приложения разбрасывал артефакты сборки в виде папок -mix, вместо того чтобы все аккуратненько сложить в одну папочку build. А потом автор уволился оставив после себя кучу самописных скриптов на все случаи жизни: генерация спрайтов, автоимпорты стилуса, авторезолв зависимостей вместо нормальной модульной системы. Это было бы еще ничего, если зависимости были где-то четко зафиксированы. вместо этого была куча магии, самописная модульная система, которой не пользовался никто кроме автора. одним словом была та еще морока разобраться в этой куче, т.к. документации автор тоже после себя не оставил.

      Поэтому спасибо, но нет.


      1. vintage
        31.12.2015 16:46
        +5

        Ну а я наелся конфигурационных систем типа RequireJS, где по идентификатору модуля не понятно где его искать, потому как где-то в стороне лежит конфиг с маппингом пространств имён (и то в лучшем случае, а бывает и с маппингом конкретных идентификаторов) на файловую систему. Наелся сборщиков типа gulp, где отдельно собираются css, отдельно js и отдельно шаблоны, отдельно картинки, всё отдельно, при этом фиг скажешь, что вот этому вот стайлшиту требуется подключённый модернайзер, а когда нужно собрать из тех же исходников другой вариант приложения приходится всё это копипастить. Наелся сборщиков типа webpack, где подключение стилей и шаблонов происходит из js, даже если собственно js модулю не нужен. Впрочем, он ближе всех подошёл к идеалу.

        Я это к чему — то, что вы считаете «нормальной модульной системой» другие считают ущербной. И не по религиозным соображениям, как некоторые, а потому, что видят недостатки и знают как их можно устранить. Например, в PHP есть стандарт расположения файлов PSR-0, благодаря ему, по идентификатору сущности всегда понятно откуда её брать. Это позволяет не копипастить 100500 импортов в каждый файл — достаточно расположить код по стандарту и его можно спокойно использовать из любой части программы. Зависимость резолвится автоматически по факту использования. Особый маразм можно наблюдать в языке Go, где у вас программа с неиспользуемыми зависимостями просто не скомпилируется. Закомментировали блок, чтобы подебажить, ан-нет, получайте ошибку компиляции — идите и комментируйте импорты. А если компилятор/сборщик и так знает что используется модулем, то зачем дополнительно вручную указывать, что вы это «импортируете»? Это бесполезная бюрократия, а не «нормальная модульная система».

        А нормальная модульная система не должна зависеть от форматов файлов, потому как модуль — это не просто js или css файл. Модуль — это группа файлов (зачастую js+css+шаблон+картинки) и их имеет смысл подключать либо все, либо вообще не подключать, если модуль нигде по факту не используется.

        Можно подумать, остальные 8 мегабайт кода приложения были хорошо документированы. В моём коде хоть jsdoc-и были.


        1. k12th
          31.12.2015 18:14

          О каком PSR может идти речь, если до сих пор идут такие баталии как «не называйте файлы кэмелкейсом». И пока какого-нибудь вменяемого или хотя бы единого JSR не будет, автолоад лучше даже не начинать.
          В конфигурационных системах обычно есть хотя бы документация и, в конце концов, в этот конфиг можно заглянуть. В голову автора автолоадера не заглянешь, а если и заглянешь, то пожалеешь.


          1. vintage
            31.12.2015 19:54

            Пока не начнёшь он не появится. PSR-0 появился как стандартизация устоявшейся практики. Promises в яваскрипте появились как стандартизация устоявшейся практики. А если бы мы сидели и ждали стандарта, то никогда бы его не дождались.

            Я как бы и не спорил, что документация нужна и важна.


        1. qtuz
          09.01.2016 01:07

          Наелся сборщиков типа webpack, где подключение стилей и шаблонов происходит из js, даже если собственно js модулю не нужен.

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


          1. vintage
            09.01.2016 02:53

            Вы никогда не писали модули без единой строчки яваскрипта? Например, вынесенный partial template со своими стилями, или набор stylus примесей, зависящий от модернайзера, или описание компоненты на специальном декларативном языке.


  1. erlyvideo
    31.12.2015 14:49
    +12

    Это очень, просто ужасно плохой пример и ужасная практика.

    Так делать категорически нельзя! Все подобные вещи живут в голове неделю, максимум две. Потом автор этих строк увольняется и уходит на следующую работу, а человек вместо него пытается понять, откуда у него определена переменная Drizzer (а это оказался моднейший новый фреймворк, про который ещё нет статьи на хабре)


    1. AllRight88
      31.12.2015 15:05
      -1

      Явное, безусловно, лучше неявного. Но так можно сказать про любые практики :). «Зачем нам использовать sass, если разработчик уволится, придет новый и ничего не поймет». Подразумевается, что перед началом работы новый разработчик онакомится с проктом — хотя бы webpack конфиг прочитает на предмет используемых модулей. А то ведь новичка и линтер удивит, и автобилд, и еще много всего «неизвестного ему».


      1. erlyvideo
        31.12.2015 16:14

        Да, меня удивляет куча всякого полурабочего кода, появившегося за последние полтора года вокруг яваскрипта.

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

        Меня очень злило, когда я искал react-canvas для браузера, но ни в одном репозитории не было даже README в котором было бы написано, как в этом сезоне модно компилировать яваскрипт в пригодный для использования код.


    1. jakobz
      05.01.2016 14:39

      Ну, уж $ и _ — так точно можно подключить. Гораздо хуже если будут вариации потом: в одном файле import * as $ from «jquery», в другом —
      import * as jquery from «jquery».


  1. k12th
    31.12.2015 14:51
    +7

    А все-таки явное лучше неявного. Да и IDE'шка с eslint'ом будет ругаться, мол, откуда это взялось, а если писать /* globals $, _, React */ так весь смысл пропадает.


    1. AllRight88
      31.12.2015 15:07

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


      1. k12th
        31.12.2015 15:14
        +3

        Надеюсь, что нет. Сегодня eslint, завтра что-то еще, сегодня вебпак, завтра еще какое-нибудь дуо. Не надо еще и тулзы гвоздями прибивать друг к другу.


  1. AndersonDunai
    31.12.2015 15:03
    +9

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

    Тем более, многие IDE (от тех же JetBrains) умеют группировать импорты в одну строку.

    Мне кажется, импорты — чуть ли не единственное, что должно быть указано явно («explicitly»). Да, JS — гибкий язык, но не стоит этим злоупотреблять.

    Это чисто моё субьективное мнение.


    1. AllRight88
      31.12.2015 15:08
      +1

      Использовать или нет — безусловно, личный выбор каждой команды. В любом случае, согласитесь, что знать о такой возможности webpack довольно полезно. При встрече с таким подходом в каком-нибудь проекте на github будет сразу понятно, что там происходит.


  1. baka_cirno
    31.12.2015 15:28
    +6

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


    1. k12th
      31.12.2015 18:17
      +1

      +1. Боролись-боролись с глобалами и вот нате.


      1. vintage
        31.12.2015 19:56

        А вы с какой целью с ними боролись?


        1. k12th
          01.01.2016 13:56

      1. Artem_Govorov
        01.01.2016 05:25

        Боролись не с глобалами как таковыми, а с отсутствием/ущербностью альтернативы.


        1. k12th
          01.01.2016 13:55

          Глобальные переменные, помимо риска коллизий, это еще и неявные зависимости.


          1. vintage
            01.01.2016 14:39
            -1

            Идентификаторы модулей в импортах — не более чем уникальные ключи в глобальной переменной. Так что от коллизий имён вас это не спасёт. А спасают от этого пространства имён. И зависимости вполне себе явные в обоих случаях.


          1. YChebotaev
            01.01.2016 16:15
            -1

            А какой может быть риск коллизий? Если у вас на проекте используется, например, реакт и лодаш, то довольно странно было бы иметь локальные переменные React и _, которые ссылались бы на что-нибудь другое кроме реакта и лодаша.
            Проблема глобальных переменных сильно преувеличена. Да, объявлять глобальные переменные внутри модулей плохая практика, потому что это усложняет рефакторинг, но вот иметь глобальные переменные, от которых зависит проект целиком, наоборот, только упрощает дело. Разумеется, это не относится к библиотекам и изолированным частям системы. Но если у вас есть 200 ангуляр контроллеров, единственная задача которых — это удовлетворение бизнес требований, и они никому не нужны вне текущего проекта то и ничего плохого в этом нет.


  1. RayMan
    31.12.2015 15:30
    +10

    common.js

    import $ from '$';
    import JSON from 'JSON';
    import _ from '_';
    import Q from 'Q';
    import moment from 'moment';
    
    export {$, JSON, _, Q, moment};
    


    index.js

    import {$, _} from 'common';
    
    $('#app').hide();
    


  1. Houston
    31.12.2015 16:47
    +3

    ProvidePlugin лучше использовать для того, чтобы передовать в js конфигурационные параметры типа DEBUG


    1. Elfet
      04.01.2016 09:10
      +1

      DefinePlugin.