2015-02-17 11 views
46

Ich habe einige Dinge für die Entwicklung - z. B. Mocks, die ich meine verteilte Build-Datei mit nicht aufblähen möchte.Bedingtes Build basierend auf der Umgebung mit Webpack

In RequireJS können Sie eine Konfiguration in einer Plugin-Datei übergeben und bedingt davon abhängig Dinge benötigen.

Für Webpack scheint es keine Möglichkeit zu geben, dies zu tun. Zum einen eine Laufzeitkonfiguration für eine Umgebung zu schaffen I resolve.alias verwendet haben, um eine Abhängigkeit von der Umgebung repoint erfordern, zum Beispiel:

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

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

Wenn dann die webpack Config Erstellen I dynamisch, welche Datei envsettings Punkte (dh webpackConfig.resolve.alias.envsettings = './' + env) zuweisen .

Allerdings würde Ich mag, wie etwas zu tun ist:

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

Aber natürlich will ich nicht in diesen Mock-Dateien erstellen, wenn die Umgebung verspotten nicht.

Ich könnte möglicherweise alle diese Anforderungen zu einer Stub-Datei mit replace.alias wieder manuell repoint - aber gibt es eine Möglichkeit, die weniger Hacky fühlt?

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

+0

Beachten Sie, dass für jetzt habe ich Alias ​​des verwendeten auf eine leere (stub) Datei auf Umgebungen zeigen, die ich nicht will (zB require ('mocks') zeigt auf eine leere Datei auf nicht-mock envs. Scheint ein wenig hacky, aber es funktioniert. –

Antwort

38

Sie können die define plugin verwenden.

Ich benutze es, um etwas so einfache wie dies in Ihrer webpack Build-Datei zu tun, wo env ist der Pfad zu einer Datei, die ein Objekt von 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 dieses in Ihrem Code

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

Es wird diesen Code aus Ihrer Build-Datei entfernen, wenn die Bedingung falsch ist. Sie können eine funktionierende Webpack build example here sehen.

+0

Ich bin ein wenig verwirrt durch diese Lösung.Es erwähnt nicht, wie ich 'env' einstellen soll.Sehen Sie durch dieses Beispiel scheint es, als ob sie diese Flagge über Gul handhaben p und yargs, die nicht jeder benutzt. – Andre

+1

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

+2

@mark ja. Fügen Sie etwas wie "globals": {"ENV": true} 'zu Ihrem .eslintrc –

14

eine andere Art und Weise unter Verwendung einer JS-Datei als proxy, und lassen Sie die Datei, um das Modul von Interesse in commonjs laden und exportieren es als es2015 module, wie folgt aus:

// 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 ES2015-Modul verwenden, in Ihre Anwendung normalerweise:

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

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

+1

das ist nicht notwendig, wenn Sie in Ihrer Webpack-Datei das Plugin 'webpack.optimize.UglifyJsPlugin()' verwenden, wird die Optimierung von Webpack das Modul nicht laden, da der Zeilencode innerhalb der Bedingung immer falsch ist, also Webpack entfernen es aus dem generierten Bundle –

+0

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

8

landete ich mit etwas ähnliches wie Matt Derrick' Answer, bis aber machte sich Sorgen um zwei Punkte:

  1. Die vollständige Konfiguration wird jedes Mal injiziert, wenn ich ENV verwende (was für große Konfigurationen schlecht ist).
  2. Ich muss mehrere Einstiegspunkte definieren, weil require(env) auf verschiedene Dateien zeigt.

Was kam ich mit einem einfachen Komponist, die ein Config-Objekt baut und spritzt es zu einem Config-Modul.
Hier ist die Dateistruktur, Iam für diese Verwendung:

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

Die main.js alle Sachen Standardkonfiguration enthält:

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

module.exports = mainConfig; 

Die dev.js und production.js nur Config Sachen halten, die die Haupt-Konfigurationsüberschreibt:

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

module.exports = devConfig; 

Der wichtige Teil ist die webpack.config.js welche komponiert die config und verwendet die DefinePlugin eine Umgebungsvariable __APP_CONFIG__, die das zusammengesetzte Config-Objekt hält zu generieren:

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 die config.js, es sieht wie folgt aus (es6 Import-Export-Syntax hier, weil seine Verwendung unter webpack):

const config = __APP_CONFIG__; 

export default config; 

In Ihrem app.js Sie jetzt import config from './config'; bekommen die Config-Objekt verwenden können.

7

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

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

Die entsprechende webpack Konfiguration

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

Nicht sicher ist, warum die „webpack.DefinePlugin“ tun Antwort der obersten für die Definition Umwelt basierend Importe/erfordert überall .

Das Problem mit diesem Ansatz ist, dass Sie immer noch alle diese Module an den Client liefern -> zum Beispiel mit webpack-bundle-analyezer überprüfen. Und nicht Ihre bundle.js Größe reduziert haupt :)

Also wirklich, was gut funktioniert und viel logischer ist: NormalModuleReplacementPlugin

Also anstatt ein on_client bedingten zu tun erfordern -> ist einfach nicht nicht benötigte Dateien auf dem bündeln in erster Linie

Hoffnung, die

+0

Nice wusste nicht über dieses Plugin! –

+0

Mit diesem Szenario hätten Sie nicht mehrere Builds pro Umgebung? Wenn ich zum Beispiel eine Web-Service-Adresse für dev/QA/UAT/Produktionsumgebungen hätte, würde ich dann 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. –

+0

Nein, nicht wirklich. Genau das machen Sie mit dem Plugin -> Sie spezifizieren Ihre Umgebung durch env vars 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 es ist nicht das, worum es bei diesem Plugin geht und was es tut. –

0

hilft Während dies nicht die beste Lösung ist, kann es für einige Ihrer Bedürfnisse arbeiten. Wenn Sie verschiedene Code in Knoten und Browser ausgeführt werden mit diesem für mich gearbeitet:

if (typeof window !== 'undefined') 
    return 
} 
//run node only code now 
+0

OP fragt nach einer Kompilierzeit-Entscheidung, Ihre Antwort bezieht sich auf die Laufzeit. – Michael

Verwandte Themen