Bedingter Build basierend auf der Umgebung mit Webpack

Lesezeit: 9 Minuten

Benutzer-Avatar
Dominik

Ich habe einige Dinge für die Entwicklung – zB Mocks, mit denen ich meine verteilte Build-Datei nicht aufblähen möchte.

In RequireJS können Sie eine Konfiguration in einer Plugin-Datei übergeben und darauf basierend Dinge bedingt anfordern.

Für Webpack scheint es keine Möglichkeit zu geben, dies zu tun. Erstens, um eine Laufzeitkonfiguration für eine von mir verwendete Umgebung zu erstellen auflösen.alias um eine Anforderung abhängig von der Umgebung neu auszurichten, z. B.:

// All settings.
var all = {
    fish: 'salmon'
};

// `envsettings` is an alias resolved at build time.
module.exports = Object.assign(all, require('envsettings'));

Dann kann ich beim Erstellen der Webpack-Konfiguration dynamisch zuweisen, welche Datei envsettings weist auf (z webpackConfig.resolve.alias.envsettings="./" + env).

Allerdings würde ich gerne sowas machen:

if (settings.mock) {
    // Short-circuit ajax calls.
    // Require in all the mock modules.
}

Aber natürlich möchte ich diese Mock-Dateien nicht einbauen, wenn die Umgebung nicht Mock ist.

Ich könnte möglicherweise alle diese Anforderungen manuell mithilfe von resolve.alias erneut auf eine Stub-Datei verweisen – aber gibt es einen Weg, der sich weniger hacky anfühlt?

Irgendwelche Ideen, wie ich das machen kann? Vielen Dank.

  • Beachten Sie, dass ich vorerst Aliase verwendet habe, um auf eine leere (Stub-) Datei in Umgebungen zu verweisen, die ich nicht möchte (z. B. require(‘mocks’) zeigt auf eine leere Datei in Nicht-Mock-Envs. Scheint ein wenig hacky, aber es funktioniert.

    – Dominik

    10. März 2015 um 10:24 Uhr

Du kannst den … benutzen Plugin definieren.

Ich verwende es, indem ich etwas so Einfaches in Ihrer Webpack-Build-Datei wo mache env ist der Pfad zu einer Datei, die ein Objekt mit Einstellungen exportiert:

// 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 };

und dann das in deinem Code

if (ENV.debug) {
    console.log('Yo!');
}

Dieser Code wird aus Ihrer Build-Datei entfernt, wenn die Bedingung falsch ist. Sie können eine Arbeit sehen Webpack-Build-Beispiel hier.

  • Ich bin ein wenig verwirrt von dieser Lösung. Es wird nicht erwähnt, wie ich einstellen soll env. Wenn man sich dieses Beispiel ansieht, scheint es, als würden sie dieses Flag über Gulp und Yargs handhaben, die nicht jeder verwendet.

    – André

    14. April 2016 um 23:59 Uhr


  • Wie funktioniert das mit Linters? Müssen Sie neue globale Variablen, die im Define-Plugin hinzugefügt werden, manuell definieren?

    – Kennzeichen

    2. Mai 2016 um 5:21 Uhr

  • @mark ja. Fügen Sie etwas hinzu wie "globals": { "ENV": true } zu Ihrer .eslintrc

    – Matt Derrick

    2. Mai 2016 um 20:29 Uhr


  • Wie würde ich auf die ENV-Variable in einer Komponente zugreifen? Ich habe die obige Lösung ausprobiert, aber ich erhalte immer noch den Fehler, dass ENV nicht definiert ist

    – jasan

    9. September 2016 um 11:09 Uhr

  • Der Code wird NICHT aus den Build-Dateien entfernt! Ich habe es getestet und der Code ist da.

    – Lionel

    2. März 2017 um 15:42 Uhr

Ich bin mir nicht sicher, warum die Antwort “webpack.DefinePlugin” überall die beste ist, um umgebungsbasierte Importe/Anforderungen zu definieren.

Das Problem Bei diesem Ansatz liefern Sie immer noch alle diese Module an den Client -> prüfen Sie mit webpack-bundle-analyezer zum Beispiel. Und die Größe Ihrer bundle.js überhaupt nicht reduzieren 🙂

Was also wirklich gut funktioniert und viel logischer ist, ist: NormalModuleReplacementPlugin

Anstatt also eine on_client-bedingte Anforderung auszuführen -> nehmen Sie nicht benötigte Dateien überhaupt nicht in das Bundle auf

Ich hoffe, das hilft

  • Nice kannte dieses Plugin nicht!

    – Dominik

    9. Juli 2017 um 12:01 Uhr

  • Hätten Sie bei diesem Szenario nicht mehrere Builds pro Umgebung? Wenn ich beispielsweise eine Webdienstadresse für Entwicklungs-/QA-/UAT-/Produktionsumgebungen habe, würde ich 4 separate Container benötigen, 1 für jede Umgebung. Idealerweise haben Sie einen Container und starten ihn mit einer Umgebungsvariablen, um anzugeben, welche Konfiguration geladen werden soll.

    – Brett Mathe

    13. September 2017 um 11:38 Uhr

  • Nein nicht wirklich. Genau das machen Sie mit dem Plugin -> Sie geben Ihre Umgebung durch env vars an und es baut nur einen Container, aber für eine bestimmte Umgebung ohne redundante Einschlüsse. Natürlich hängt das auch davon ab, wie Sie Ihre Webpack-Konfiguration einrichten, und natürlich können Sie alle Builds erstellen, aber das ist nicht das, worum es bei diesem Plugin geht und was es tut.

    – Roman Zhyliov

    20. September 2017 um 3:18 Uhr

  • @RomanZhyliov Was ist, wenn ich ein npm-Paket basierend auf clientseitigen Fehlern importieren muss? Ich denke, dieses Plugin wird nicht funktionieren, oder?

    – Nevin Madhukar K

    22. September 2021 um 9:34 Uhr

Verwenden ifdef-loader. In Ihren Quelldateien können Sie Dinge tun wie

/// #if ENV === 'production'
console.log('production!');
/// #endif

Das relevante webpack Konfiguration ist

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}`,
        },
      },
    ],
  },
  // ...
};

  • Ich habe diese Antwort positiv bewertet, da die akzeptierte Antwort den Code nicht wie erwartet entfernt und die Präprozessor-ähnliche Syntax eher als bedingtes Element identifiziert wird.

    – Christian Ivicevic

    20. April 2018 um 13:29 Uhr

  • Vielen Dank! Es wirkt wie ein Zauber. Mehrere Stunden Experimente mit ContextReplacementPlugin, NormalModuleReplacementPlugin und anderen Sachen – alle fehlgeschlagen. Und hier ist der ifdef-Loader, der mir den Tag rettet.

    – jeron-diovis

    10. Dezember 2018 um 14:41 Uhr

Benutzer-Avatar
ofhouse

Am Ende habe ich etwas Ähnliches wie Matt Derrick’ Answer verwendet, war aber besorgt über zwei Punkte:

  1. Die komplette Config wird bei jeder Nutzung injiziert ENV (Was für große Konfigurationen schlecht ist).
  2. Ich muss mehrere Einstiegspunkte definieren, weil require(env) zeigt auf verschiedene Dateien.

Was ich mir ausgedacht habe, ist ein einfacher Composer, der ein Konfigurationsobjekt erstellt und es in ein Konfigurationsmodul einfügt.
Hier ist die Dateistruktur, die ich dafür verwende:

config/
 └── main.js
 └── dev.js
 └── production.js
src/
 └── app.js
 └── config.js
 └── ...
webpack.config.js

Das main.js enthält alle Standardkonfigurationen:

// main.js
const mainConfig = {
  apiEndPoint: 'https://api.example.com',
  ...
}

module.exports = mainConfig;

Das dev.js und production.js enthält nur Konfigurationsmaterial, das die Hauptkonfiguration überschreibt:

// dev.js
const devConfig = {
  apiEndPoint: 'http://localhost:4000'
}

module.exports = devConfig;

Der wichtige Teil ist die webpack.config.js die die Konfiguration erstellt und die verwendet Plugin definieren um eine Umgebungsvariable zu generieren __APP_CONFIG__ das das zusammengesetzte Konfigurationsobjekt enthält:

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))
    })
  ]
};

Der letzte Schritt ist nun der config.jssieht es so aus (unter Verwendung der es6-Import-Export-Syntax hier, weil es sich unter Webpack befindet):

const config = __APP_CONFIG__;

export default config;

In deiner app.js könntest du jetzt verwenden import config from './config'; um das Konfigurationsobjekt zu erhalten.

Eine andere Möglichkeit ist die Verwendung einer JS-Datei als proxyund lassen Sie diese Datei das gewünschte Modul laden commonjsund exportieren Sie es als es2015 moduleso was:

// 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

Dann können Sie das ES2015-Modul normal in Ihrer App verwenden:

// myApp.js
import { myString } from './store/myModule.js'
myString // <- "this is in dev"

  • Das einzige Problem mit if/else und require besteht darin, dass beide erforderlichen Dateien in der generierten Datei gebündelt werden. Ich habe keine Problemumgehung gefunden. Im Wesentlichen erfolgt zuerst das Bündeln, dann das Mangeln.

    – Alex

    12. April 2016 um 18:02 Uhr

  • das ist nicht unbedingt wahr, wenn Sie das Plugin in Ihrer Webpack-Datei verwenden webpack.optimize.UglifyJsPlugin()lädt die Optimierung von webpack das Modul nicht, da der Zeilencode innerhalb der Bedingung immer falsch ist, sodass webpack es aus dem generierten Bundle entfernt

    – Alejandro Silva

    13. April 2016 um 12:23 Uhr


  • @AlejandroSilva hast du ein Repo-Beispiel dafür?

    – Kapuziner

    15. Juli 2016 um 8:47 Uhr

  • @thevangelist ja: github.com/AlejandroSilva/mototracker/blob/master/… Es ist ein Node + React + Redux-Haustierprojekt: P

    – Alejandro Silva

    16. Juli 2016 um 23:15 Uhr

Benutzer-Avatar
Paul Whippe

Konfrontiert mit dem gleichen Problem wie das OP und aufgrund der Lizenzierung verpflichtet, bestimmten Code in bestimmten Builds nicht aufzunehmen, habe ich das übernommen webpack-conditional-loader folgendermaßen:

In meinem Build-Befehl setze ich eine Umgebungsvariable passend für meinen Build. Zum Beispiel „demo“ in package.json:

...
  "scripts": {
    ...
    "buildDemo": "./node_modules/.bin/webpack --config webpack.config/demo.js --env.demo --progress --colors",
...

Das verwirrende Bit, das in der Dokumentation, die ich gelesen habe, fehlt, ist das Ich muss dies während der gesamten Build-Verarbeitung sichtbar machen indem ich sicherstelle, dass meine env-Variable in den Prozess global eingefügt wird, also in meine 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};

Damit kann ich alles bedingt ausschließen und sicherstellen, dass der zugehörige Code ordnungsgemäß aus dem resultierenden JavaScript ausgeschüttelt wird. Zum Beispiel wird in my routes.js der Demo-Inhalt so von anderen Builds ferngehalten:

... // #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
...

This works with webpack 4.29.6.

  • The only problem with if/else and require is that both required files will be bundled into the generated file. I haven’t found a workaround. Essentially bundling happens first, then mangling.

    – alex

    Apr 12, 2016 at 18:02

  • that’s not necesary true, if you use in your webpack file the plugin webpack.optimize.UglifyJsPlugin(), the optimization of webpack won’t load the module, as the line code inside the conditional is always false, so webpack remove it from the generated bundle

    – Alejandro Silva

    Apr 13, 2016 at 12:23


  • @AlejandroSilva do you have a repo example of this?

    – Capuchin

    Jul 15, 2016 at 8:47

  • @thevangelist yep: github.com/AlejandroSilva/mototracker/blob/master/… it’s a node+react+redux pet proyect 😛

    – Alejandro Silva

    Jul 16, 2016 at 23:15

I’ve struggled with setting env in my webpack configs. What I usually want is to set env so that it can be reached inside webpack.config.js, postcss.config.js and inside the entry point application itself (index.js usually). I hope that my findings can help someone.

The solution that I’ve come up with is to pass in --env production or --env development, and then set mode inside webpack.config.js.
However, that doesn’t help me with making env accessible where I want it (see above), so I also need to set process.env.NODE_ENV explicitly, as recommended here.
Most relevant part that I have in webpack.config.js follow below.

...
module.exports = mode => {
  process.env.NODE_ENV = mode;

  if (mode === "production") {
    return merge(commonConfig, productionConfig, { mode });
  }
  return merge(commonConfig, developmentConfig, { mode });
};

1210100cookie-checkBedingter Build basierend auf der Umgebung mit Webpack

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy