Это вторая часть нашего поста Переход к 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.