Passaggio di variabili dipendenti dall'ambiente nel webpack


306

Sto cercando di convertire un'app angolare da gulp a webpack. in gulp uso gulp-preprocess per sostituire alcune variabili nella pagina html (es. nome del database) a seconda di NODE_ENV. Qual è il modo migliore per ottenere un risultato simile con il webpack?


1
Alias ​​ha funzionato per te?
Juho Vepsäläinen,

1
@bebraw: prima di essere in grado di aggirare gli alias, ho implementato l'altra soluzione che mi hai suggerito sulla base di DefinePlugin (). Ora vedo che l'alias sarebbe una soluzione migliore e probabilmente refatterà qualche volta - grazie. Se desideri includere le tue due soluzioni in una risposta, accetterò felicemente.
kpg,

2
È stato diretto qui tramite il messaggio della console. Come risolvere questo problema in Browserify?
GN.

2
Questa domanda sta cercando di configurare la SPA in fase di compilazione o di caricamento? Prendo atto di due tipi di configurazione per le SPA: 1) modalità di sviluppo o produzione e 2) ambiente di distribuzione, ad esempio sviluppo, gestione temporanea, produzione. Penso che NODE_ENV possa essere utilizzato per configurare per (1) al momento della compilazione, ma come possiamo configurarlo per (2) al momento dell'implementazione, ad esempio configurando una modalità di produzione per diversi ambienti di distribuzione. Spero che questo sia rilevante per questa domanda.
Ashley Aitken,

1
@AshleyAitken Grande domanda di cui non sono riuscito a trovare una risposta su questa discussione (forse mi sono perso), ma ho pubblicato questa nuova discussione: stackoverflow.com/questions/44464504/…
David Tesar

Risposte:


427

Ci sono due modi di base per raggiungere questo obiettivo.

DefinePlugin

new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}),

Nota che questo sostituirà semplicemente le partite "così come sono". Ecco perché la stringa è nel formato in cui si trova. Potresti avere una struttura più complessa, come un oggetto lì, ma ottieni l'idea.

EnvironmentPlugin

new webpack.EnvironmentPlugin(['NODE_ENV'])

EnvironmentPluginutilizza DefinePlugininternamente e mappa i valori dell'ambiente per codificarlo. Sintassi di Terser.

Alias

In alternativa, è possibile utilizzare la configurazione tramite un modulo con alias . Dal lato del consumatore sembrerebbe così:

var config = require('config');

La configurazione stessa potrebbe apparire così:

resolve: {
    alias: {
        config: path.join(__dirname, 'config', process.env.NODE_ENV)
    }
}

Diciamo che lo process.env.NODE_ENVè development. Quindi mapperebbe ./config/development.js. Il modulo su cui mappa può esportare la configurazione in questo modo:

module.exports = {
    testing: 'something',
    ...
};

3
Grazie per aver sottolineato il fatto che sostituisce le partite "così come sono". Stavo lottando da un po 'per capire perché il mio codice generava un errore ed era perché non stavo avvolgendo il valore in aJSON.stringify()
pbojinov il

4
Se stai usando ES2015, puoi anche usare l'interpolazione di stringhe -'process.env.NODE_ENV': `"${process.env.NODE_ENV || 'development'}"`
user2688473

1
@ tybro0103 JSON.stringify('development')come è potrebbe non essere veramente utile. Invece JSON.stringify(someVariable)può essere abbastanza!
superjos

1
Dovresti iniziare NODE_ENVa farlo. Come impostarlo dipende dalla tua piattaforma.
Juho Vepsäläinen,

1
@AnyulRivas Yeah. React usa il process.env.NODE_ENVmodello e funziona.
Juho Vepsäläinen,

109

Solo un'altra opzione, se si desidera utilizzare solo un'interfaccia cli, basta usare l' defineopzione del webpack. Aggiungo il seguente script nel mio package.json:

"build-production": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors"

Quindi devo solo correre npm run build-production.


2
C'è documentazione per questo? Non riesco a Google --define :(
Richard,

5
Per webpack @ 2, "-p" è già una scorciatoia per --optimize-minim --define process.env.NODE_ENV = "production"
okm

@okm Documenti menziona -p Equivale a --ottimizzare-minimizzare --ottimizzare-occorrenza-ordine, quindi nessuna menzione di --define process.env.NODE_ENV = "produzione". È qualcosa che è stato rimosso?
Nader Ghanbari,

1
@NaderHadjiGhanbari È nella webpack versione 2 webpack.js.org/api/cli/#shortcuts
okm

73

Ho studiato un paio di opzioni su come impostare variabili specifiche dell'ambiente e ho finito con questo:

Al momento ho 2 configurazioni Webpack:

webpack.production.config.js

new webpack.DefinePlugin({
  'process.env':{
    'NODE_ENV': JSON.stringify('production'),
    'API_URL': JSON.stringify('http://localhost:8080/bands')
  }
}),

webpack.config.js

new webpack.DefinePlugin({
  'process.env':{
    'NODE_ENV': JSON.stringify('development'),
    'API_URL': JSON.stringify('http://10.10.10.10:8080/bands')
  }
}),

Nel mio codice ottengo il valore di API_URL in questo (breve) modo:

const apiUrl = process.env.API_URL;

EDIT 3 novembre, 2016

I documenti di Webpack hanno un esempio: https://webpack.js.org/plugins/define-plugin/#usage

new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true),
    VERSION: JSON.stringify("5fa3b9"),
    BROWSER_SUPPORTS_HTML5: true,
    TWO: "1+1",
    "typeof window": JSON.stringify("object")
})

Con ESLint è necessario consentire specificatamente variabili non definite nel codice, se si ha la no-undefregola. http://eslint.org/docs/rules/no-undef in questo modo:

/*global TWO*/
console.log('Running App version ' + TWO);

EDIT 7 set 2017 (Create-React-App specific)

Se non ti va di configurare troppo, dai un'occhiata a Create-React-App: Create-React-App - Aggiunta di variabili d'ambiente personalizzate . Sotto il cofano CRA utilizza comunque Webpack.


2
Hai scoperto che ciò ha impedito il passaggio di eventuali variabili di ambiente in fase di esecuzione? Se si sostituisce l'intero process.env, process.env.PORTad esempio, non si risolve undefineddurante la creazione del webpack, il che significa che non è più possibile ignorare la porta dall'ambiente?
djskinner,

Grazie mille. Finalmente una risposta su questo problema che è comprensibile!
Dave Sag,

che cos'è il processo? da dove viene? se è un oggetto nodo, come entra nel browser?
Daniel Birowsky Popeski,

Questa è una soluzione terribile, hai due webpack.configs quasi del tutto identici, tranne per l'impostazione di NODE_ENV e API_URL
Brian Ogden,

1
@BrianOgden Sì, è vero, dovresti usare qualcosa come webpack-merge per questo: npmjs.com/package/webpack-merge - È un po 'fuori portata per questa domanda IMO.
thevangelist,

24

Puoi passare qualsiasi argomento della riga di comando senza plugin aggiuntivi usando --envda webpack 2:

webpack --config webpack.config.js --env.foo=bar

Utilizzando la variabile in webpack.config.js:

module.exports = function(env) {
    if (env.foo === 'bar') {
        // do something
    }
}

fonte


22

È possibile utilizzare direttamente il EnvironmentPlugindisponibile in webpackper accedere a qualsiasi variabile di ambiente durante la traspilazione.

Devi solo dichiarare il plugin nel tuo webpack.config.jsfile:

var webpack = require('webpack');

module.exports = {
    /* ... */
    plugins = [
        new webpack.EnvironmentPlugin(['NODE_ENV'])
    ]
};

Si noti che è necessario dichiarare esplicitamente il nome delle variabili di ambiente che si desidera utilizzare.


4
C'è un esempio nei documenti webpack con questo caso d'uso. github.com/webpack/docs/wiki/list-of-plugins#environmentplugin
Technetium

1
Se si desidera inserire le variabili di ambiente in un file .env, è possibile utilizzare il pacchetto dotenv e inizializzarlo in webpack.config.js. npmjs.com/package/dotenv
Justin McCandless,

13

Per aggiungere personalmente al gruppo di risposte preferisco quanto segue:

const webpack = require('webpack');
const prod = process.argv.indexOf('-p') !== -1;

module.exports = {
  ...
  plugins: [
    new webpack.DefinePlugin({
      process: {
        env: {
          NODE_ENV: prod? `"production"`: '"development"'
        }
      }
    }),
    ...
  ]
};

Usando questo non ci sono problemi env funky o multipiattaforma (con env vars). Tutto ciò che fai è eseguire il normale webpacko webpack -pper lo sviluppo o la produzione rispettivamente.

Riferimento: problema di Github


Quando si definiscono i valori per il processo preferire 'process.env.NODE_ENV': JSON.stringify('production')oltre process: { env: { NODE_ENV: JSON.stringify('production') } }. L'utilizzo di quest'ultimo sovrascriverà l'oggetto di processo che può interrompere la compatibilità con alcuni moduli che prevedono la definizione di altri valori sull'oggetto di processo.
slorenzo,

13

Dal momento che la mia modifica al post precedente di thevangelist non è stata approvata , pubblicando ulteriori informazioni.

Se si desidera selezionare il valore da package.json come un numero di versione definito e accedervi tramite DefinePlugin all'interno di Javascript.

{"version": "0.0.1"}

Quindi, importare package.json all'interno del rispettivo webpack.config , accedere all'attributo utilizzando la variabile di importazione, quindi utilizzare l'attributo nel DefinePlugin .

const PACKAGE = require('../package.json');
const _version = PACKAGE.version;//Picks the version number from package.json

Ad esempio, una certa configurazione è attiva webpack.config utilizza METADATA per DefinePlugin:

const METADATA = webpackMerge(commonConfig({env: ENV}).metadata, {
  host: HOST,
  port: PORT,
  ENV: ENV,
  HMR: HMR,
  RELEASE_VERSION:_version//Version attribute retrieved from package.json
});

new DefinePlugin({
        'ENV': JSON.stringify(METADATA.ENV),
        'HMR': METADATA.HMR,
        'process.env': {
          'ENV': JSON.stringify(METADATA.ENV),
          'NODE_ENV': JSON.stringify(METADATA.ENV),
          'HMR': METADATA.HMR,
          'VERSION': JSON.stringify(METADATA.RELEASE_VERSION)//Setting it for the Scripts usage.
        }
      }),

Accedi a questo all'interno di qualsiasi file dattiloscritto:

this.versionNumber = process.env.VERSION;

Il modo più intelligente sarebbe così:

// webpack.config.js
plugins: [
    new webpack.DefinePlugin({
      VERSION: JSON.stringify(require("./package.json").version)
    })
  ]

Grazie a Ross Allen


11

Solo un'altra risposta simile alla risposta di @ zer0chain. Tuttavia, con una distinzione.

L'impostazione webpack -pè sufficiente.

È lo stesso di:

--define process.env.NODE_ENV="production"

E questo è lo stesso di

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  //...

  plugins:[
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    })
  ]
};

Quindi potresti aver bisogno solo di qualcosa del genere nel package.jsonfile Node:

{
  "name": "projectname",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "debug": "webpack -d",
    "production": "webpack -p"
  },
  "author": "prosti",
  "license": "ISC",
  "dependencies": {    
    "webpack": "^2.2.1",
    ...
  }
}

Solo alcuni suggerimenti da DefinePlugin :

DefinePlugin consente di creare costanti globali che possono essere configurate in fase di compilazione. Ciò può essere utile per consentire comportamenti diversi tra build di sviluppo e build di rilascio. Ad esempio, è possibile utilizzare una costante globale per determinare se avviene la registrazione; forse esegui il log nella build di sviluppo ma non nella build di rilascio. Questo è il tipo di scenario facilitato da DefinePlugin.


Che sia così puoi controllare se scrivi webpack --help

Config options:
  --config  Path to the config file
                         [string] [default: webpack.config.js or webpackfile.js]
  --env     Enviroment passed to the config, when it is a function

Basic options:
  --context    The root directory for resolving entry point and stats
                                       [string] [default: The current directory]
  --entry      The entry point                                          [string]
  --watch, -w  Watch the filesystem for changes                        [boolean]
  --debug      Switch loaders to debug mode                            [boolean]
  --devtool    Enable devtool for better debugging experience (Example:
               --devtool eval-cheap-module-source-map)                  [string]
  -d           shortcut for --debug --devtool eval-cheap-module-source-map
               --output-pathinfo                                       [boolean]
  -p           shortcut for --optimize-minimize --define
               process.env.NODE_ENV="production" 

                      [boolean]
  --progress   Print compilation progress in percentage                [boolean]

3

Per aggiungere al gruppo di risposte:

Utilizzare ExtendedDefinePlugin invece di DefinePlugin

npm install extended-define-webpack-plugin --save-dev.

ExtendedDefinePlugin è molto più semplice da usare ed è documentato :-) link

Poiché DefinePlugin non ha una buona documentazione, voglio dare una mano, dicendo che in realtà funziona come #DEFINE in c # .

#if (DEBUG)
        Console.WriteLine("Debugging is enabled.");
#endif

Quindi, se vuoi capire come funziona DefinePlugin, leggi la doucmentation c # #define. collegamento


2

Preferisco usare il file .env per un ambiente diverso.

  1. Utilizzare webpack.dev.config per copiare env.devin .env nella cartella principale
  2. Utilizzare webpack.prod.config per copiare env.prodin .env

e nel codice

uso

require('dotenv').config(); const API = process.env.API ## which will store the value from .env file


2

Ho trovato la seguente soluzione per essere la più facile da impostare variabile d'ambiente per Webpack 2:

Ad esempio abbiamo le impostazioni di un webpack:

var webpack = require('webpack')

let webpackConfig = (env) => { // Passing envirmonment through
                                // function is important here
    return {
        entry: {
        // entries
        },

        output: {
        // outputs
        },

        plugins: [
        // plugins
        ],

        module: {
        // modules
        },

        resolve: {
        // resolves
        }

    }
};

module.exports = webpackConfig;

Aggiungi variabile d'ambiente nel Webpack:

plugins: [
    new webpack.EnvironmentPlugin({
       NODE_ENV: 'development',
       }),
]

Definisci variabile plug-in e aggiungila a plugins:

    new webpack.DefinePlugin({
        'NODE_ENV': JSON.stringify(env.NODE_ENV || 'development')
    }),

Ora quando esegui il comando webpack, passa env.NODE_ENVcome argomento:

webpack --env.NODE_ENV=development

// OR

webpack --env.NODE_ENV development

Ora puoi accedere alle NODE_ENVvariabili ovunque nel tuo codice.


1

Dal momento che Webpack v4, semplicemente impostando modela configurazione del tuo Webpack verrà impostato NODE_ENVper te (tramite DefinePlugin). Documenti qui.


1

Ecco un modo che ha funzionato per me e mi ha permesso di mantenere le variabili di ambiente ASCIUTTE riutilizzando un file json.

const webpack = require('webpack');
let config = require('./settings.json');
if (__PROD__) {
    config = require('./settings-prod.json');
}

const envVars = {};
Object.keys(config).forEach((key) => {
    envVars[key] = JSON.stringify(config[key]);
});

new webpack.DefinePlugin({
    'process.env': envVars
}),

0

Non sono un grande fan di ...

new webpack.DefinePlugin({
  'process.env': envVars
}),

... in quanto non fornisce alcun tipo di sicurezza. invece, finisci per potenziare le tue cose segrete, a meno che tu non aggiunga un webpack a gitignore 🤷‍♀️ c'è una soluzione migliore.

Fondamentalmente con questa configurazione una volta compilato il codice tutte le variabili env del processo saranno rimosse dall'intero codice, non ci sarà un singolo processo.env.VAR grazie al plug-in babel transform-inline-environment-variables PS se non vuoi finire con un sacco di undefine, assicurati di chiamare env.js prima che il webpack chiami babel-loader, ecco perché è la prima cosa che chiama il webpack. l'array di var nel file babel.config.js deve corrispondere all'oggetto su env.js. ora c'è solo una cosa da fare. aggiungi un .envfile inserisci tutte le tue variabili env, il file deve essere alla radice del progetto o sentiti libero di aggiungerlo dove vuoi, assicurati di impostare la stessa posizione sul file env.js e aggiungilo anche a gitignore

const dotFiles = ['.env'].filter(Boolean);

if (existsSync(dotFiles)) {
    require("dotenv-expand")(require("dotenv").config((dotFiles)));
}

Se vuoi vedere l'intera babel + webpack + ts prenderla da heaw https://github.com/EnetoJara/Node-typescript-babel-webpack.git

e la stessa logica si applica per reagire e tutto il resto 💩

config
---webpack.js
---env.js
src
---source code world
.env
bunch of dotFiles

env.js

"use strict";
/***
I took the main idea from CRA, but mine is more cooler xD
*/
const {realpathSync, existsSync} = require('fs');
const {resolve, isAbsolute, delimiter} = require('path');

const NODE_ENV = process.env.NODE_ENV || "development";

const appDirectory = realpathSync(process.cwd());

if (typeof NODE_ENV !== "string") {
    throw new Error("falle and stuff");
}

const dotFiles = ['.env'].filter(Boolean);

if (existsSync(dotFiles)) {
    require("dotenv-expand")(require("dotenv").config((dotFiles)));
}

process.env.NODE_PATH = (process.env.NODE_PATH || "")
    .split(delimiter)
    .filter(folder => folder && isAbsolute(folder))
    .map(folder => resolve(appDirectory, folder))
    .join(delimiter);

const ENETO_APP = /^ENETO_APP_/i;

module.exports = (function () {
    const raw = Object.keys ( process.env )
        .filter ( key => ENETO_APP.test ( key ) )
        .reduce ( ( env, key ) => {
                env[ key ] = process.env[ key ];
                return env;
            },
            {
                BABEL_ENV: process.env.ENETO_APP_BABEL_ENV,
                ENETO_APP_DB_NAME: process.env.ENETO_APP_DB_NAME,
                ENETO_APP_DB_PASSWORD: process.env.ENETO_APP_DB_PASSWORD,
                ENETO_APP_DB_USER: process.env.ENETO_APP_DB_USER,
                GENERATE_SOURCEMAP: process.env.ENETO_APP_GENERATE_SOURCEMAP,
                NODE_ENV: process.env.ENETO_APP_NODE_ENV,
                PORT: process.env.ENETO_APP_PORT,
                PUBLIC_URL: "/"
            } );

    const stringyField = {
        "process.env": Object.keys(raw).reduce((env, key)=> {
            env[key]=JSON.stringify(raw[key]);
            return env;
        },{}),

    };

    return {
        raw, stringyField
    }
})();

file webpack senza troll plug-in

"use strict";

require("core-js");
require("./env.js");

const path = require("path");
const nodeExternals = require("webpack-node-externals");

module.exports = env => {
    return {
        devtool: "source-map",
        entry: path.join(__dirname, '../src/dev.ts'),
        externals: [nodeExternals()],
        module: {
            rules: [
                {
                    exclude: /node_modules/,
                    test: /\.ts$/,
                    use: [
                        {
                            loader: "babel-loader",
                        },
                        {
                            loader: "ts-loader"
                        }
                    ],
                },
                {
                    test: /\.(png|jpg|gif)$/,
                    use: [
                        {
                            loader: "file-loader",
                        },
                    ],
                },
            ],
        },
        node: {
            __dirname: false,
            __filename: false,
        },
        optimization: {
            splitChunks: {
                automaticNameDelimiter: "_",
                cacheGroups: {
                    vendor: {
                        chunks: "initial",
                        minChunks: 2,
                        name: "vendor",
                        test: /[\\/]node_modules[\\/]/,
                    },
                },
            },
        },
        output: {
            chunkFilename: "main.chunk.js",
            filename: "name-bundle.js",
            libraryTarget: "commonjs2",
        },
        plugins: [],
        resolve: {
            extensions: ['.ts', '.js']
        }   ,
        target: "node"
    };
};

babel.config.js

module.exports = api => {

    api.cache(() => process.env.NODE_ENV);

    return {

        plugins: [
            ["@babel/plugin-proposal-decorators", { legacy: true }],
            ["@babel/plugin-transform-classes", {loose: true}],
            ["@babel/plugin-external-helpers"],
            ["@babel/plugin-transform-runtime"],
            ["@babel/plugin-transform-modules-commonjs"],
            ["transform-member-expression-literals"],
            ["transform-property-literals"],
            ["@babel/plugin-transform-reserved-words"],
            ["@babel/plugin-transform-property-mutators"],
            ["@babel/plugin-transform-arrow-functions"],
            ["@babel/plugin-transform-block-scoped-functions"],
            [
                "@babel/plugin-transform-async-to-generator",
                {
                    method: "coroutine",
                    module: "bluebird",
                },
            ],
            ["@babel/plugin-proposal-async-generator-functions"],
            ["@babel/plugin-transform-block-scoping"],
            ["@babel/plugin-transform-computed-properties"],
            ["@babel/plugin-transform-destructuring"],
            ["@babel/plugin-transform-duplicate-keys"],
            ["@babel/plugin-transform-for-of"],
            ["@babel/plugin-transform-function-name"],
            ["@babel/plugin-transform-literals"],
            ["@babel/plugin-transform-object-super"],
            ["@babel/plugin-transform-shorthand-properties"],
            ["@babel/plugin-transform-spread"],
            ["@babel/plugin-transform-template-literals"],
            ["@babel/plugin-transform-exponentiation-operator"],
            ["@babel/plugin-proposal-object-rest-spread"],
            ["@babel/plugin-proposal-do-expressions"],
            ["@babel/plugin-proposal-export-default-from"],
            ["@babel/plugin-proposal-export-namespace-from"],
            ["@babel/plugin-proposal-logical-assignment-operators"],
            ["@babel/plugin-proposal-throw-expressions"],
            [
                "transform-inline-environment-variables",
                {
                    include: [
                        "ENETO_APP_PORT",
                        "ENETO_APP_NODE_ENV",
                        "ENETO_APP_BABEL_ENV",
                        "ENETO_APP_DB_NAME",
                        "ENETO_APP_DB_USER",
                        "ENETO_APP_DB_PASSWORD",
                    ],
                },
            ],
        ],
        presets: [["@babel/preset-env",{
            targets: {
                node: "current",
                esmodules: true
            },
            useBuiltIns: 'entry',
            corejs: 2,
            modules: "cjs"
        }],"@babel/preset-typescript"],
    };
};

"Finisci per migliorare le tue cose segrete, a meno che non aggiungi un webpack a gitignore." @Ernesto puoi approfondire questo?
Katie Byers,

Fondamentalmente il tuo bundle finisce senza il process.env.BLAHBLAH e mette il valore effettivo. Ad esempio, invece di avere process.env.NODE_ENV, finisci con "produzione", intendo che non è l'esempio migliore, ma immagina una chiave segreta. Il tuo bundle avrà il valore reale e chissà cosa significa quella stringa cablata 🤷‍♀️
Ernesto

Hmmm - sì, quei valori saranno interpolati nella versione costruita , ma presumibilmente non lo stai spingendo su GitHub ...
Katie Byers,

-4

Non so perché, ma nessuno menziona davvero la soluzione più semplice. Questo funziona per me per nodejs e grunt. Come per molte persone il webpack può creare confusione, puoi semplicemente usare la riga seguente:

process.env.NODE_ENV = 'production';

Con la soluzione di cui sopra non hai davvero bisogno di usare envify o webpack. A volte la semplice soluzione hardcoded può funzionare per alcune persone.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.