Передача змінних в середовищі змінних у webpack


306

Я намагаюся перетворити кутовий додаток з gulp в webpack. в gulp я використовую gulp-препроцесс для заміни деяких змінних на html-сторінці (наприклад, ім'я бази даних) залежно від NODE_ENV. Який найкращий спосіб досягти подібного результату за допомогою webpack?


1
Чи працював псевдонім для вас?
Juho Vepsäläinen

1
@bebraw: перед тим, як мені вдалося обійняти псевдоніми, я реалізував інше запропоноване вами рішення, засноване на DefinePlugin (). Зараз я бачу, що псевдонім стане кращим рішенням і, можливо, коли-небудь відмовиться - дякую. Якщо ви хочете включити свої два рішення у відповідь, я з радістю прийму це.
kpg

2
Тут було направлено через консольне повідомлення. Як це виправити в Browserify?
GN.

2
Це питання намагається налаштувати SPA під час створення або завантаження? Я зазначу два типи конфігурації для СПА: 1) розробка або режим виробництва та 2) середовище розгортання, наприклад, розробка, постановка, виробництво. Я думаю, що NODE_ENV можна використовувати для налаштування (1) під час збирання, але як ми налаштовуємо на (2) при розгортанні, наприклад, налаштування режиму виробництва для різних середовищ розгортання. Я сподіваюся, що це стосується цього питання.
Ешлі Айткен

1
@AshleyAitken Велике запитання, на яке я не зміг знайти відповідь на цю тему (можливо, я пропустив її), але опублікував цю нову тему: stackoverflow.com/questions/44464504/…
Девід Тезар

Відповіді:


427

Є два основних способи досягти цього.

Визначте плагін

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

Зауважте, що це просто замінить сірники "як є". Ось чому рядок у такому форматі, який він є. Ви могли б мати складнішу структуру, таку як об'єкт, але ви розумієте.

Плагін Environment

new webpack.EnvironmentPlugin(['NODE_ENV'])

EnvironmentPluginвикористовує DefinePluginвнутрішньо і відображає значення середовища для кодування через нього. Терстер-синтаксис.

Псевдонім

Крім того, ви можете споживати конфігурацію через псевдонім модуль . З боку споживача це виглядатиме так:

var config = require('config');

Сама конфігурація може виглядати так:

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

Скажімо Давайте process.env.NODE_ENVє development. Це буде відображатись ./config/development.jsтоді. Модуль, на який відображається, може експортувати конфігурацію так:

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

3
Дякуємо, що вказали на те, що він замінює сірники "як є". Я деякий час намагався зрозуміти, чому мій код кидає помилку, і це було тому, що я не перетворював значення вJSON.stringify()
pbojinov

4
Якщо ви використовуєте ES2015, ви також можете використовувати інтерполяцію рядків -'process.env.NODE_ENV': `"${process.env.NODE_ENV || 'development'}"`
user2688473

1
@ tybro0103 JSON.stringify('development')як це може бути не дуже корисно. Натомість JSON.stringify(someVariable)цілком може бути!
superjos

1
Вам слід NODE_ENVце зробити. Як встановити, що залежить від вашої платформи.
Juho Vepsäläinen

1
@AnyulRivas Так. React використовує process.env.NODE_ENVшаблон, і він працює.
Juho Vepsäläinen

109

Ще один варіант, якщо ви хочете використовувати лише інтерфейс cli, просто скористайтеся defineопцією webpack. Я додаю такий сценарій у свій package.json:

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

Тому мені просто бігти npm run build-production.


2
Чи є на це документація? Я не можу Google - визначити :(
Річард

5
Для webpack @ 2 "-p" вже є ярликом для --optimize-minimize --define process.env.NODE_ENV = "виробництво"
okm

@okm Документи згадують -p дорівнює --оптимізувати-мінімізувати --оптимізувати-порядок виникнення, тому жодної згадки про --define process.env.NODE_ENV = "виробництво". Це щось видалено?
Надер Ганбарі

1
@NaderHadjiGhanbari Це у версії webpack 2 webpack.js.org/api/cli/#shortcuts
okm

73

Я дослідив пару варіантів того, як встановити змінні, що стосуються середовища, і закінчив це:

Зараз у мене є 2 конфігурації веб-пакету:

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')
  }
}),

У своєму коді я отримую значення API_URL таким (коротким) способом:

const apiUrl = process.env.API_URL;

EDIT 3 листопада 2016 року

Документи Webpack мають приклад: 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")
})

За допомогою ESLint вам потрібно спеціально дозволити невизначені змінні в коді, якщо у вас є no-undefправила. http://eslint.org/docs/rules/no-undef ось так:

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

EDIT 7 вересня 2017 р. (Конкретно для створення додатків)

Якщо ви не дуже налаштовані, перевірте Create-React-App: Create-React-App - Додавання користувацьких змінних середовищ . Під капотом CRA так чи інакше використовує Webpack.


2
Чи виявили ви, що це перешкоджало передачі змінних середовищ під час виконання? Якщо ви замінюєте ціле, process.envтоді, process.env.PORTнаприклад, не вирішується undefinedпід час створення веб-пакету, це означає, що ви більше не можете змінювати порт з оточення?
djskinner

Дуже дякую. Нарешті відповідь на це питання, зрозуміла!
Дейв Саг

що таке процес? звідки воно походить? якщо це об’єкт вузла, як він потрапляє в браузер?
Данило Біровський Попески

Це жахливе рішення, у вас є дві webpack.configs майже повністю однакові, за винятком встановлення NODE_ENV та API_URL
Брайан Огден,

1
@BrianOgden Так, це дійсно, ви повинні використовувати для цього щось на зразок webpack-merge: npmjs.com/package/webpack-merge - Це питання трохи не вдається для цього питання IMO.
thevangelist

24

Ви можете передавати будь -який аргумент командного рядка без додаткових плагінів, використовуючи --envз webpack 2:

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

Використання змінної в webpack.config.js:

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

Джерело


22

Ви можете безпосередньо використовувати EnvironmentPluginдоступне в, webpackщоб мати доступ до будь-якої змінної середовища під час транспіляції.

Вам просто потрібно оголосити плагін у вашому webpack.config.jsфайлі:

var webpack = require('webpack');

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

Зауважте, що ви повинні чітко оголосити ім'я змінних середовища, які ви хочете використовувати.


4
У документі webpack є приклад із таким випадком використання. github.com/webpack/docs/wiki/list-of-plugins#environmentplugin
технецій

1
Якщо ви хочете помістити змінні середовища у файл .env, ви можете використовувати пакет dotenv та ініціалізувати його у webpack.config.js. npmjs.com/package/dotenv
Джастін МакКандлесс

13

Щоб додати до групи відповідей особисто, я віддаю перевагу наступному:

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

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

Використовуючи це, немає жодних проблем із змінною env чи міжплатформою (з env vars). Все, що ви робите, це запустити нормальний webpackабо відповідно webpack -pдля розробників або виробництва.

Довідка: Випуск Github


При визначенні значень для процесу віддайте перевагу 'process.env.NODE_ENV': JSON.stringify('production')над process: { env: { NODE_ENV: JSON.stringify('production') } }. Використання останнього перезаписає об'єкт процесу, що може порушити сумісність з деякими модулями, які очікують визначення інших значень об'єкта процесу.
slorenzo

13

Оскільки мою редакцію на вищезгаданій публікації Thevangelist не затвердили , розміщуючи додаткову інформацію.

Якщо ви хочете вибрати значення з package.json, як визначений номер версії та отримати доступ до нього через DefinePlugin всередині Javascript.

{"version": "0.0.1"}

Потім імпортуйте package.json у відповідний webpack.config , відкрийте атрибут за допомогою змінної імпорту, а потім використовуйте атрибут у DefinePlugin .

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

Наприклад, певна конфігурація на webpack.config використовує METADATA для 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.
        }
      }),

Доступ до цього всередині будь-якого файлу машинопису:

this.versionNumber = process.env.VERSION;

Найрозумніший спосіб був би таким:

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

Завдяки Россу Аллену


11

Ще одна відповідь, схожа на відповідь @ zer0chain. Однак, з однією відмінністю.

Налаштування webpack -pдостатньо.

Це те саме, що:

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

І це те саме, що

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

module.exports = {
  //...

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

Тож вам може знадобитися лише щось подібне у package.jsonфайлі 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",
    ...
  }
}

Кілька порад з DefinePlugin :

DefinePlugin дозволяє створювати глобальні константи, які можна налаштувати під час компіляції. Це може бути корисно для дозволу різної поведінки між розробками та випусками. Наприклад, ви можете використовувати глобальну константу, щоб визначити, чи відбувається ведення журналу; можливо, ви виконуєте журнал у своїй розробці, але не у версії. Ось такий сценарій полегшує DefinePlugin.


Це так, ви можете перевірити, чи вводите ви 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

Щоб додати до групи відповідей:

Використовуйте ExtendedDefinePlugin замість DefinePlugin

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

ExtendedDefinePlugin набагато простіший у використанні і задокументовано :-) посилання

Оскільки у DefinePlugin бракує гарної документації, я хочу допомогти, сказавши, що він насправді працює як #DEFINE в c # .

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

Таким чином, якщо ви хочете зрозуміти, як працює DefinePlugin, прочитайте подвійну документацію c # #define. посилання


2

Я вважаю за краще використовувати .env файл для іншого середовища.

  1. Використовуйте webpack.dev.config для копіювання env.devу .env у кореневу папку
  2. Використовуйте webpack.prod.config для копіювання env.prodв .env

і в коді

використання

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


2

Я знайшов наступне рішення найпростішим у налаштуванні змінної середовища для Webpack 2:

Наприклад, у нас є налаштування веб-пакету:

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;

Додати змінну середовища в Webpack:

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

Визначте змінну плагіна та додайте її до plugins:

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

Тепер при запуску команди webpack передайте env.NODE_ENVяк аргумент:

webpack --env.NODE_ENV=development

// OR

webpack --env.NODE_ENV development

Тепер ви можете отримати доступ до NODE_ENVзмінної в будь-якому місці вашого коду.



1

Ось спосіб, який працював для мене і дозволив мені зберегти змінні середовища DRY шляхом повторного використання файлу 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

Я не великий фанат ...

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

... оскільки це не забезпечує жодного типу безпеки. замість цього, ви в кінцевому підсумку збільшуєте свої секретні речі, якщо ви не додасте веб-пакет для gitignore 🤷‍♀️ є краще рішення.

В основному з цим конфігурацією після того, як ви скомпілюєте свій код, всі змінні процесу env будуть видалені з усього коду, не буде єдиного process.env.VAR вгору завдяки плагіну transform-inline-environment-variables PS Babel, якщо ви не хочете закінчувати з цілою купою невизначених даних, переконайтеся, що ви зателефонували до env.js перед тим, як webpack зателефонував за допомогою завантажувача, тому саме це перше, що викликає webpack. масив vars у файлі babel.config.js повинен відповідати об’єкту в env.js. тепер є лише одна косина справа. Додайте .envфайл, помістіть туди всі ваші змінні env, файл повинен бути в корені проекту або не соромтесь додати його там, де вам завгодно, просто переконайтеся, що ви встановите те саме місце у файлі env.js, а також додайте його до gitignore

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

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

Якщо ви хочете побачити весь babel + webpack + ts, дістаньте його з Heaw https://github.com/EnetoJara/Node-typescript-babel-webpack.git

і така ж логіка застосовується для реагування та всіх інших 💩

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

файл вебпакету без тролів плагінів

"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"],
    };
};

"Ви в кінцевому підсумку збільшуєте свої секретні речі, якщо ви не додасте веб-пакет для gitignore." @Ernesto Ви можете взагалі розширити це?
Кеті Байерс

В основному ваш пакет закінчується процесом.env.BLAHBLAH і ставить фактичне значення. Наприклад, замість того, щоб Process.env.NODE_ENV закінчувати "виробництвом", я маю на увазі, що це не найкращий приклад, але уявіть секретний ключ. Ваш пакет буде мати фактичне значення, і хто знає, що означає ця дротова струна 🤷‍♀️
Ернесто,

Гммм - так, ці значення будуть інтерпольовані у вбудованій версії, але, мабуть, ви не підштовхуєте це до GitHub ...
Katie Byers

-4

Я не знаю, чому, але ніхто насправді не згадує про найпростіше рішення. Це для мене працює для nodejs та grunt. Що стосується багатьох людей, веб-пакет може бути заплутаним, ви можете просто скористатись наступним рядком:

process.env.NODE_ENV = 'production';

З вищезазначеним рішенням вам не потрібно використовувати envify або webpack. Іноді просте рішення з жорстким кодом може працювати для деяких людей.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.