Webpackを使用した環境に基づく条件付きビルド


93

私はいくつかの開発用のものを持っています-例えば、分散ビルドファイルを膨らませたくないモック。

RequireJSでは、プラグインファイルで設定を渡し、それに基づいて条件付きで条件を要求できます。

Webpackの場合、これを行う方法がないようです。まず、環境のランタイム構成を作成するために、resolve.aliasを使用して、環境に応じてrequireを再ポイントしました。例:

// 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を使用して、これらすべての必要なファイルを手動でスタブファイルに再ポイントすることもできます。

どうすればそれを行うことができますか?ありがとう。


ここでは、エイリアスを使用して、不要な環境で空の(スタブ)ファイルをポイントしていることに注意してください(例:require( 'mocks')は、非モック環境で空のファイルをポイントします。少しハックに見えますが、作品
ドミニク

回答:


59

defineプラグインを使用できます

私は、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!');
}

条件がfalseの場合、ビルドファイルからこのコードが削除されます。ここで動作するWebpackビルドの例を見ることができます


私はこの解決策に少し混乱しています。どのように設定するかについては触れられていませんenv。その例を見ると、誰もが使用しているわけではないgulpおよびyargsを介してそのフラグを処理しているように見えます。
Andre

1
これはリンターでどのように機能しますか?定義プラグインに追加される新しいグローバル変数を手動で定義する必要がありますか?
マーク

2
@マークはい。"globals": { "ENV": true }.eslintrc に次のようなものを追加してください
Matt Derrick

コンポーネントのENV変数にどのようにアクセスしますか?上記の解決策を試しましたが、ENVが定義されていないというエラーが引き続き表示されます
jasan

18
ビルドファイルからコードを取り除きません!私はそれをテストし、コードはここにあります。
ライオネル

42

「webpack.DefinePlugin」の回答が、環境ベースのインポート/要件を定義するためにどこでもトップの回答である理由がわかりません。

このアプローチの問題は、これらのモジュールをすべてクライアントに配信し続けていることです。たとえば、webpack-bundle-analyezerで確認してください。そして、あなたのbundle.jsのサイズをまったく減らしません:)

したがって、実際にうまく機能し、より論理的になるのは、NormalModuleReplacementPluginです。

したがって、on_client条件付きrequireを実行するのではなく、まず、不要なファイルをバンドルに含めないでください。

それが役に立てば幸い


ニースはそのプラグインを知りませんでした!
ドミニク

このシナリオでは、環境ごとに複数のビルドを作成しませんか?たとえば、開発/ QA / UAT /本番環境用のWebサービスアドレスがある場合、4つの個別のコンテナーが必要です。それぞれの環境に1つです。理想的には、1つのコンテナーがあり、どの構成をロードするかを指定する環境変数を使用してそれを起動します。
Brett Mathe 2017

いいえ、そうでもありません。それはまさにあなたがプラグインで行うことです->あなたはenv varsを通してあなたの環境を指定し、それは1つのコンテナだけをビルドしますが、冗長なインクルージョンのない特定の環境のために。もちろん、これはwebpack設定のセットアップ方法にも依存し、明らかにすべてのビルドをビルドできますが、これはこのプラグインの目的や機能ではありません。
ローマZhyliov 2017

34

を使用し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}`,
        },
      },
    ],
  },
  // ...
};

2
受け入れられた回答は期待どおりにコードを削除せず、プリプロセッサーのような構文が条件付き要素として識別される可能性が高いため、この回答を支持しました。
Christian Ivicevic

1
本当にありがとう!それは魅力のように働きます。ContextReplacementPlugin、NormalModuleReplacementPlugin、およびその他のものを使用した数時間の実験–すべて失敗しました。そして、これがifdef-loaderです。
jeron-diovis

27

私はマットデリックの回答に似たものを使用することになりましたが、2つの点が心配されました。

  1. 使用するたびに完全な構成が挿入されます ENV(大規模な構成の、これは良くありません)。
  2. require(env)異なるファイルを指すため、複数のエントリポイントを定義する必要があります。

私が思いついたのは、構成オブジェクトを作成して構成モジュールに挿入する単純なコンポーザーです。
これがIamが使用するファイル構造です。

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
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';して構成オブジェクトを取得できます。


2
本当にここで最高の答え
ガブリエル

18

別の方法は、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"

19
if / elseおよびrequireの唯一の問題は、両方の必要なファイルが生成されたファイルにバンドルされることです。回避策が見つかりません。基本的にバンドルが最初に行われ、次にマングルが行われます。
アレックス

2
これは必須ではありません。webpackファイルでプラグインを使用すると webpack.optimize.UglifyJsPlugin()、条件内の行コードは常にfalseになるため、webpackの最適化はモジュールをロードしません。したがって、webpackは生成されたバンドルからそれを削除します
Alejandro Silva

@AlejandroSilvaこれのリポジトリの例はありますか?
伝道者2016

1
@thevangelistうん:github.com/AlejandroSilva/mototracker/blob/master/…それはノード+反応+ Reduxペットプロジェクトです:P
アレハンドロシルバ

4

OPと同じ問題に直面し、特定のビルドに特定のコードを含めないように、ライセンスのために、私はwebpack-conditional-loaderを採用しましたは次のように。

ビルドコマンドでは、ビルドに適した環境変数を設定しています。たとえば、package.jsonの「demo」:

...
  "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から適切に振り落とされます。たとえば私のroutes.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
...

これは、webpack 4.29.6で動作します。


1
より多くの機能を備えたgithub.com/dearrrfish/preprocess-loaderもあります
user9385381

1

私は自分のwebpack設定でenvを設定するのに苦労しました。私が通常欲しいのは、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 });
};


-1

これは最善の解決策ではありませんが、ニーズによってはうまくいく場合があります。これを使用してノードとブラウザで異なるコードを実行したい場合は、私にとってうまくいきました:

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

1
OPはコンパイル時の決定について尋ねています。答えは実行時についてです。
マイケル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.