Подтвердить что ты не робот

Условная сборка на основе среды с помощью 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 - но есть ли способ, который кажется менее взломанным?

Любые идеи, как я могу это сделать? Спасибо.

4b9b3361

Ответ 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

В итоге я использовал что-то похожее на "Ответ Мэтта Деррика", но беспокоился о двух моментах:

  1. Полная конфигурация вводится каждый раз, когда я использую ENV (что плохо для больших конфигов).
  2. Я должен определить несколько точек входа, потому что 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