Условная сборка на основе среды с помощью Webpack
У меня есть некоторые вещи для разработки - например, mocks, которые я бы не хотел раздувать мой распределенный файл сборки.
В RequireJS вы можете передать конфигурацию в файле плагина и, конечно же, потребовать от нее вещи на основе этого.
Для webpack, похоже, нет способа сделать это. Во-первых, чтобы создать конфигурацию среды выполнения для среды, я использовал resolve.alias для повторного вызова в зависимости от среды, например:
// All settings.
var all = {
fish: 'salmon'
};
// `envsettings` is an alias resolved at build time.
module.exports = Object.assign(all, require('envsettings'));
Затем при создании конфигурации webpack я могу динамически назначать, какой файл envsettings
указывает на (т.е. webpackConfig.resolve.alias.envsettings = './' + env
).
Однако я хотел бы сделать что-то вроде:
if (settings.mock) {
// Short-circuit ajax calls.
// Require in all the mock modules.
}
Но, очевидно, я не хочу создавать эти файлы-макеты, если среда не макет.
Я мог бы вручную перенаправить все, что требуется, в файл заглушки, снова используя resolve.alias - но есть ли способ, который кажется менее взломанным?
Любые идеи, как я могу это сделать? Спасибо.
Ответы
Ответ 1
Вы можете использовать определить плагин.
Я использую его, делая что-то столь же простое, как это, в файле сборки webpack, где env
- это путь к файлу, который экспортирует объект настроек:
// Webpack build config
plugins: [
new webpack.DefinePlugin({
ENV: require(path.join(__dirname, './path-to-env-files/', env))
})
]
// Settings file located at `path-to-env-files/dev.js`
module.exports = { debug: true };
а затем это в вашем коде
if (ENV.debug) {
console.log('Yo!');
}
Он отключит этот код из вашего файла сборки, если условие ложно. Здесь вы можете увидеть рабочий пример сборки Webpack.
Ответ 2
Не уверен, почему ответ "webpack.DefinePlugin" является самым верным для определения импорта/импорта на основе среды.
Проблема с этим подходом заключается в том, что вы доставляете все эти модули клиенту → проверяйте webpack-bundle- анализатор. И не уменьшая размер вашего пакета .js вообще:)
Итак, что действительно работает хорошо и гораздо логичнее: NormalModuleReplacementPlugin
Поэтому вместо того, чтобы сделать условие on_client обязательным → просто не включать ненужные файлы в пакет в первую очередь
Надеюсь, что поможет
Ответ 3
Используйте ifdef-loader
. В ваших исходных файлах вы можете делать такие вещи, как
/// #if ENV === 'production'
console.log('production!');
/// #endif
Соответствующая конфигурация webpack
const preprocessor = {
ENV: process.env.NODE_ENV || 'development',
};
const ifdef_query = require('querystring').encode({ json: JSON.stringify(preprocessor) });
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'ifdef-loader?${ifdef_query}',
},
},
],
},
// ...
};
Ответ 4
В итоге я использовал что-то похожее на "Ответ Мэтта Деррика", но беспокоился о двух моментах:
- Полная конфигурация вводится каждый раз, когда я использую
ENV
(что плохо для больших конфигов). - Я должен определить несколько точек входа, потому что
require(env)
указывает на разные файлы.
Я придумал простой композитор, который создает объект конфигурации и внедряет его в модуль конфигурации.
Вот структура файла, которую я использую для этого:
config/
└── main.js
└── dev.js
└── production.js
src/
└── app.js
└── config.js
└── ...
webpack.config.js
main.js
содержит все настройки по умолчанию:
// main.js
const mainConfig = {
apiEndPoint: 'https://api.example.com',
...
}
module.exports = mainConfig;
В dev.js
и production.js
хранятся только dev.js
конфигурации, которые переопределяют основную конфигурацию:
// dev.js
const devConfig = {
apiEndPoint: 'http://localhost:4000'
}
module.exports = devConfig;
Важной частью является webpack.config.js
который составляет конфигурацию и использует DefinePlugin для генерации переменной среды __APP_CONFIG__
которая содержит составленный объект конфигурации:
const argv = require('yargs').argv;
const _ = require('lodash');
const webpack = require('webpack');
// Import all app configs
const appConfig = require('./config/main');
const appConfigDev = require('./config/dev');
const appConfigProduction = require('./config/production');
const ENV = argv.env || 'dev';
function composeConfig(env) {
if (env === 'dev') {
return _.merge({}, appConfig, appConfigDev);
}
if (env === 'production') {
return _.merge({}, appConfig, appConfigProduction);
}
}
// Webpack config object
module.exports = {
entry: './src/app.js',
...
plugins: [
new webpack.DefinePlugin({
__APP_CONFIG__: JSON.stringify(composeConfig(ENV))
})
]
};
Последним шагом теперь является config.js
, он выглядит следующим образом (здесь используется синтаксис экспорта-импорта es6, потому что он находится в webpack):
const config = __APP_CONFIG__;
export default config;
В вашем app.js
вы теперь можете использовать import config from './config';
чтобы получить объект конфигурации.
Ответ 5
другой способ использует JS файл как proxy
, и пусть этот файл загружает интересующий модуль в commonjs
и экспортирует его как es2015 module
, например:
// file: myModule.dev.js
module.exports = "this is in dev"
// file: myModule.prod.js
module.exports = "this is in prod"
// file: myModule.js
let loadedModule
if(WEBPACK_IS_DEVELOPMENT){
loadedModule = require('./myModule.dev.js')
}else{
loadedModule = require('./myModule.prod.js')
}
export const myString = loadedModule
Затем вы можете обычно использовать модуль ES2015 в своем приложении:
// myApp.js
import { myString } from './store/myModule.js'
myString // <- "this is in dev"
Ответ 6
Столкнувшись с той же проблемой, что и OP, и из-за необходимости лицензирования не включать определенный код в определенные сборки, я применил webpack-conditional-loader следующим образом:
В моей команде сборки я установил переменную окружения для своей сборки. Например, 'demo' в package.json:
...
"scripts": {
...
"buildDemo": "./node_modules/.bin/webpack --config webpack.config/demo.js --env.demo --progress --colors",
...
Недоразумение, которое отсутствует в документации, которую я прочитал, заключается в том, что я должен сделать это видимым во время обработки сборки, гарантируя, что моя переменная env будет введена в глобальный процесс, таким образом, в моем webpack.config/demo.js:
/* The demo includes project/reports action to access placeholder graphs.
This is achieved by using the webpack-conditional-loader process.env.demo === true
*/
const config = require('./production.js');
config.optimization = {...(config.optimization || {}), minimize: false};
module.exports = env => {
process.env = {...(process.env || {}), ...env};
return config};
Имея это в виду, я могу условно исключить все, что гарантирует, что любой связанный код будет корректно вытеснен из получающегося JavaScript. Например, в моем rout.js демо-контент хранится вне других сборок, таким образом:
...
// #if process.env.demo
import Reports from 'components/model/project/reports';
// #endif
...
const routeMap = [
...
// #if process.env.demo
{path: "/project/reports/:id", component: Reports},
// #endif
...
Это работает с веб-пакетом 4.29.6.
Ответ 7
Я боролся с установкой env в своих конфигах webpack. Обычно я хочу установить env так, чтобы он был доступен внутри webpack.config.js
, postcss.config.js
и внутри самого приложения точки входа (обычно index.js
). Я надеюсь, что мои выводы могут кому-то помочь.
Решение, которое я нашел, состоит в том, чтобы перейти в --env production
или --env development
, а затем установить режим внутри webpack.config.js
.
Однако это не помогает мне сделать env
доступным там, где я этого хочу (см. выше), поэтому мне также нужно явно установить process.env.NODE_ENV
, как рекомендовано здесь.
Наиболее значимая часть, которую я имею в webpack.config.js
, приведена ниже.
...
module.exports = mode => {
process.env.NODE_ENV = mode;
if (mode === "production") {
return merge(commonConfig, productionConfig, { mode });
}
return merge(commonConfig, developmentConfig, { mode });
};
Ответ 8
Хотя это не лучшее решение, оно может работать для некоторых ваших нужд. Если вы хотите запустить другой код в node и браузере, это сработало для меня:
if (typeof window !== 'undefined')
return
}
//run node only code now
Ответ 9
Используйте переменные окружения для создания развертываний dev и prod:
https://webpack.js.org/guides/environment-variables/