Это вторая часть нашего поста Переход к React. В первой части мы говорили об общей картине настройки нашего стека для поэтапного перехода на React. Один из разделов, который можно было бы изучить подробнее, - это то, как мы настраиваем наш файл конфигурации webpack. Легко просто скопировать и вставить существующие конфигурации и использовать их для своих целей, но мы думаем, что полезно иметь четкое представление о том, что на самом деле делает файл конфигурации.

Файл конфигурации

Итак, вот наш основной файл конфигурации:

var path = require('path');
var webpack = require('webpack');

var config = module.exports = {
    context: __dirname,
    entry: {
      'complete_react.bundle': [
        './react-ui/agent_entry.js',  
        './react-ui/admin_entry.js',  
        './react-ui/client_entry.js'
      ]
    }
};

config.output = {
    path: path.join(__dirname, 'app', 'assets', 'javascripts'),
    filename: 'react_ui_react.bundle.js',
    publicPath: '/assets'
};

config.module = {
    loaders: []
};

config.plugins = [
  new webpack.ProvidePlugin({
    moment: 'moment',
    _: 'lodash'
  })
];

config.resolve = {
    extensions: ['', '.js', '.jsx', '.scss'],
    modulesDirectories: [ 'node_modules']
};

И наш файл конфигурации разработки:

var webpack = require('webpack');
var _ = require('lodash');
var config = module.exports = require('./main.config.js');

config = _.merge(config, {
  debug: true,
  displayErrorDetails: true,
  outputPathinfo: true,
  devtool: 'sourcemap'
});

config.entry = [
  'webpack-hot-middleware/client?path=http://0.0.0.0:8080/__webpack_hmr', // Connecting to the express hot webpack server
  'babel-polyfill',
  './react-ui/agent_entry.js', // Your appʼs entry point
  './react-ui/admin_entry.js', //Entry for admin side
  './react-ui/css_entry.js', // Entry for development CSS
  './react-ui/styleguide_entry.js'  // Entry for the styleguide
];

config.output.publicPath = 'http://localhost:8080/javascript';

config.module.loaders = [
  {
    test: /\.jsx?$/,
    loader: 'babel',
    exclude: /node_modules/,
    query: {
      "env": {
        "development": {
          "plugins": [
            ["react-transform", {
              "transforms": [{
                "transform": "react-transform-hmr",
                "imports": ["react"],
                "locals": ["module"]
              }, {
                "transform": "react-transform-catch-errors",
                "imports": ["react", "redbox-react"]
              }]
            }]
          ]
        }
      }
    }
  },
  {
    test: /\.scss$/,
    exclude: /node_modules/,
    loader: 'style!css!autoprefixer-loader?browsers=last 2 versions!sass'
  }
];

config.plugins.push(
    //new webpack.optimize.CommonsChunkPlugin('common', 'common-bundle.js')
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    // Since our JS files are run on the front-end, they won't have access to the node env
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('development')
      }
    })
);

Давайте разберемся:

Основная конфигурация

Основная конфигурация - это наш базовый уровень конфигурации - конфигурации разработки и производства используют его в качестве отправной точки и настраивают по своему усмотрению.

Требования

var path = require('path');
var webpack = require('webpack');

Хорошо, это говорит само за себя. Нам нужен путь, который является утилитой для обработки, как вы уже догадались, путей к файлам! Конечно, нам также понадобится веб-пакет.

Экспорт конфигурации

var config = module.exports = {
    context: __dirname,
    entry: {
      'complete_react.bundle': [
        './react-ui/agent_entry.js', 
        './react-ui/admin_entry.js', 
        './react-ui/client_entry.js'
      ]
    }
};

Мы используем этот раздел для настройки объекта конфигурации, который экспортируется из этого файла, чтобы мы могли потребовать его в других файлах конфигурации. Начнем с добавления двух основных аспектов конфигурации: context и entry.

Контекст - это абсолютный путь к каталогу, в котором должны храниться ваши входные файлы. В этом случае __dirname - это объект Node, который дает имя каталога, в котором находится исполняемый скрипт.

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

Путь вывода

config.output = {
    path: path.join(__dirname, 'app', 'assets', 'javascripts'),
    filename: 'react_ui_react.bundle.js',
    publicPath: '/assets'
};

Эта опция вывода дает Webpack информацию о том, как выводить скомпилированные файлы.

Путь - это абсолютный путь, по которому будут записываться ваши скомпилированные файлы. Скомпилированные файлы будут иметь имя, указанное в параметре имени файла. Наконец, параметр publicPath используется для указания общедоступного URL-адреса файлов. Это используется, когда загрузчики создают теги сценария или ссылки. Параметр publicPath будет использоваться как href тегов. В нашем случае мы хотим, чтобы publicPath был / assets, потому что мы негласно используем Rails для соответствующей маршрутизации запросов .js в каталог javascripts в папке с ресурсами.

Параметры модуля

config.module = {
    loaders: []
};

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

Плагины

config.plugins = [
  new webpack.ProvidePlugin({
    moment: 'moment',
    _: 'lodash'
  })
];

Плагины могут использоваться для обеспечения множества дополнительных функций и преимуществ помимо обычного поведения веб-пакета. В этом случае мы используем ProvidePlugin для автоматической загрузки модулей, глобально доступных в нашем пакете. Таким образом, мы назначаем содержимое модуля moment и lodash соответствующим ключам в объекте, и мы можем использовать их, не требуя их явным образом в других наших файлах.

Решимость

config.resolve = {
    extensions: ['', '.js', '.jsx', '.scss'],
    modulesDirectories: [ 'node_modules']
};

Это тоже довольно понятно. Параметр разрешения просто указывает Webpack, как искать модули. Мы говорим webpack искать файлы с определенными расширениями в каталоге node_modules. Указав расширения, мы можем использовать require (someFile) против использования require (someFile.js). Важное замечание: если вы все же включили расширение файла в свой оператор require, этот параметр должен включать пустую строку, чтобы webpack не пытался найти что-то вроде someFile.js.js

Конфигурация разработки

Здесь я расскажу об основных различиях между нашей базовой конфигурацией и нашей конфигурацией разработки.

var config = module.exports = require('./main.config.js');

config = _.merge(config, {
  debug: true,
  displayErrorDetails: true,
  outputPathinfo: true,
  devtool: 'sourcemap'
});

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

Вход

config.entry = [
  'webpack-hot-middleware/client?path=http://0.0.0.0:8080/__webpack_hmr', // Connecting to the express hot webpack server
  'babel-polyfill',
  './react-ui/agent_entry.js', // Agent entry file
  './react-ui/client_entry.js', // Client entry file
  './react-ui/admin_entry.js', // Admin entry file
  './react-ui/brokerage_entry.js', // Brokerage entry file
  './react-ui/css_entry.js', // Dev CSS entry file
  './react-ui/styleguide_entry.js'  // Styleguide entry file
];

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

Вы могли заметить, что самый первый файл записи выглядит немного странно. Это связано с тем, что вы можете передать параметр запроса во входной файл, который будет доступен для этого файла как переменная _resourceQuery.

В этом случае ? Path = http: //0.0.0.0: 8080 / __ webpack_hmr просто сообщает webpack-hot-middleware путь для поиска нашего экспресс-сервера для горячего модуля. перезарядка.

Погрузчики

config.module.loaders = [
  {
    test: /\.jsx?$/,
    loader: 'babel',
    exclude: /node_modules/,
    query: {
      "env": {
        "development": {
          "plugins": [
            ["react-transform", {
              "transforms": [{
                "transform": "react-transform-hmr",
                "imports": ["react"],
                "locals": ["module"]
              }, {
                "transform": "react-transform-catch-errors",
                "imports": ["react", "redbox-react"]
              }]
            }]
          ]
        }
      }
    }
  },
  {
    test: /\.scss$/,
    exclude: /node_modules/,
    loader: 'style!css!autoprefixer-loader?browsers=last 2 versions!sass'
  }
];

Вот в чем заключается основная часть наших различий. Эта часть может показаться немного впечатляющей, но на самом деле она довольно проста. В этом случае мы передаем массив из двух объектов в опции загрузчиков. Первый объект проверяет файлы, заканчивающиеся на .jsx, а затем использует загрузчик Babel для преобразования любого ES6 в ES5. При этом мы исключаем любые файлы jsx, которые случайно находятся в нашем каталоге node_modules. Свойство запроса объекта определяет различные плагины, которые будут использоваться с Babel в процессе производства, что позволяет выполнять горячую перезагрузку и отображение некоторых ошибок. Второй объект массива:

{
     test: /\.scss$/,
     exclude: /node_modules/,
     loader: 'style!css!autoprefixer-loader?browsers=last 2 versions!sass'
   }

фактически определяет четыре связанных загрузчика: style-loader, css-loader, autoprefixer-loader и sass-загрузчик. ! просто обозначает цепочку, и выходные данные одного загрузчика будут отправлены на вход следующего. Важное примечание: загрузчики идут справа налево! Таким образом, в этом случае любые файлы с расширением .scss будут проходить через загрузчик sass, автоматический префиксатор, затем css и, наконец, загрузчик стилей.

Еще несколько плагинов

config.plugins = config.plugins.concat([
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoErrorsPlugin(),
  // Since our JS files are run on the front-end, they won't have access to the node env
  new webpack.DefinePlugin({
    'process.env': {
      'NODE_ENV': JSON.stringify('development')
    }
  })
]);

Единственный плагин, который представляет здесь особый интерес, - это DefinePlugin. Это позволяет нам определить переменную NODE_ENV, которую ищет наш запрос загрузчика Babel, когда он решает, какие плагины использовать. В этом случае мы, по сути, устанавливаем флаг, чтобы указать, что мы находимся в нашей среде разработки.

Производство

Последний кусок головоломки - это наша производственная конфигурация, которая в основном представляет собой конфигурацию разработчика с удаленной HMR и отладкой.

Основные отличия заключаются в следующем:

Вход и выход

config.entry = {
  'agent_react.bundle': ['babel-polyfill', './react-ui/agent_entry.js'],
  'client_react.bundle': ['babel-polyfill', './react-ui/client_entry.js'],
  'admin_react.bundle': ['babel-polyfill', './react-ui/admin_entry.js'],
  'brokerage_react.bundle': ['babel-polyfill', './react-ui/brokerage_entry.js'],
  'styleguide_react.bundle': ['babel-polyfill', './react-ui/styleguide_entry.js']
};

config.output = _.merge(config.output, {
  path: path.join(config.context, 'app', 'assets', 'javascripts'),
  filename: '[name].js'
});

Как мы упоминали ранее, в процессе производства мы разделяем наши связанные файлы, чтобы мы не отправляли один огромный файл пакета клиенту. Это делается путем указания опции ввода как объекта, где ключи могут стать переменной имени в опции вывода. Значения представляют собой массив файлов, которые будут скомпилированы в один пакет. Итак, в этом случае 'agent_react.bundle': ['babel-polyfill', './react-ui/agent_entry.js'] компилируется в agent_react.bundle.js и состоит из модулей, созданных с помощью babel-polyfill, agent_entry.js , а их требуют tree.

Погрузчики

config.module.loaders.push(
    {
      test: /\.jsx?$/,
      exclude: /node_modules/,
      loaders: ['babel-loader']
    }
);

Поскольку у нас нет (или не требуется) отладки и HMR в производстве, единственный загрузчик, который нам нужен, - это Babel.

Финиш!

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

Первоначально опубликовано на eatcodeplay.com.