Webpack kullanarak ortama dayalı koşullu derleme


95

Geliştirme için bazı şeylerim var - örneğin, dağıtılmış derleme dosyamı şişirmek istemediğim taklitler.

RequireJS'de bir eklenti dosyasında bir yapılandırma iletebilir ve buna bağlı olarak bazı şeyleri şartlı olarak isteyebilirsiniz.

Web paketi için bunu yapmanın bir yolu yok gibi görünüyor. İlk olarak, bir ortam için bir çalışma zamanı yapılandırması oluşturmak için, ortama bağlı olarak bir gereksinimi yeniden işaretlemek için resol.alias kullandım , örneğin:

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

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

Daha sonra webpack yapılandırmasını oluştururken hangi dosyanın envsettingsişaret ettiğini (yani webpackConfig.resolve.alias.envsettings = './' + env) dinamik olarak atayabilirim .

Ancak şöyle bir şey yapmak isterim:

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

Ama açıkçası, ortam sahte değilse bu sahte dosyaları oluşturmak istemiyorum.

Tüm bu gereksinimleri yeniden resol.alias kullanarak bir saplama dosyasına manuel olarak yeniden işaretleyebilirim - ancak daha az kırılgan hissettiren bir yol var mı?

Bunu nasıl yapabileceğime dair bir fikrin var mı? Teşekkürler.


Şimdilik, istemediğim ortamlarda boş (saplama) bir dosyayı işaret etmek için takma adları kullandığımı unutmayın (örneğin, zorunlu ('taklitler'), sahte olmayan ortamlarda boş bir dosyayı işaret edecek. Biraz karmaşık görünüyor ama çalışır.
Dominic

Yanıtlar:


60

Tanımlama eklentisini kullanabilirsiniz .

Bunu env, bir ayarlar nesnesini dışa aktaran bir dosyanın yolu olan web paketi oluşturma dosyanızda bu kadar basit bir şey yaparak kullanıyorum :

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

ve sonra bu sizin kodunuzda

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

Koşul yanlışsa, bu kodu derleme dosyanızdan çıkaracaktır. Burada çalışan bir Web paketi oluşturma örneği görebilirsiniz .


Bu çözüm beni biraz karıştırdı. Nasıl ayarlamam gerektiğinden bahsetmiyor env. Bu örneğe bakıldığında, bu bayrağı herkesin kullanmadığı yudum ve yarglarla tutuyorlar gibi görünüyor.
Andre

1
Bu linterlerle nasıl çalışır? Define eklentisine eklenen yeni global değişkenleri manuel olarak tanımlamanız gerekiyor mu?
mark

2
@mark evet. Gibi bir şey ekleyin "globals": { "ENV": true }senin .eslintrc için
Matt Derrick

bir bileşendeki ENV değişkenine nasıl erişebilirim? Yukarıdaki çözümü denedim, ancak yine de
ENV'nin

20
Kodları yapı dosyalarından SÖKMEZ! Test ettim ve kod burada.
Lionel

42

"Webpack.DefinePlugin" yanıtının, Çevre tabanlı içe aktarımları / gereksinimleri tanımlamak için neden her yerde ilk sırada yer aldığından emin değilim.

Bu yaklaşımla ilgili sorun , hala tüm bu modülleri müşteriye teslim ediyor olmanızdır -> örneğin webpack-bundle-analyezer ile kontrol edin . Ve bundle.js boyutunuzu hiç küçültmeyin :)

Yani gerçekten iyi çalışan ve çok daha mantıklı olan şey: NormalModuleReplacementPlugin

Bu nedenle, bir on_client koşullu gerektirmek yerine -> yalnızca gerekli olmayan dosyaları ilk olarak pakete dahil etmeyin

umarım yardımcı olur


Nice bu eklentiyi bilmiyordu!
Dominic

Bu senaryoda, ortam başına birden çok derlemeniz olmaz mıydı? Örneğin, dev / QA / UAT / üretim ortamları için web hizmeti adresim varsa, her ortam için 1 tane olmak üzere 4 ayrı kapsayıcıya ihtiyacım olur. İdeal olarak, bir konteyneriniz olur ve hangi yapılandırmanın yükleneceğini belirtmek için onu bir ortam değişkeniyle başlatırsınız.
Brett Mathe

Hayır gerçek değil. Eklenti ile yaptığınız tam olarak budur -> ortamınızı env değişkenleri aracılığıyla belirlersiniz ve yalnızca bir kap oluşturur, ancak belirli ortamlar için fazlalık eklemeler olmadan. Elbette bu, web paketi yapılandırmanızı nasıl kurduğunuza da bağlıdır ve tabii ki tüm yapıları oluşturabilirsiniz, ancak bu eklentinin yaptığı ve yaptığı şey bu değildir.
Roman Zhyliov

34

Kullanın ifdef-loader. Kaynak dosyalarınızda aşağıdaki gibi şeyler yapabilirsiniz

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

İlgili webpackkonfigürasyon

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

3
Kabul edilen yanıt kodu beklendiği gibi çıkarmadığı ve önişlemci benzeri sözdiziminin koşullu bir öğe olarak tanımlanma olasılığı daha yüksek olduğu için bu yanıta olumlu oy verdim.
Christian Ivicevic

1
Çok teşekkürler! Mucizevi şekilde çalışır. ContextReplacementPlugin, NormalModuleReplacementPlugin ve diğer şeyler ile birkaç saatlik deneyler - hepsi başarısız oldu. Ve işte ifdef-loader, günümü kurtarıyor.
jeron-diovis

28

Matt Derrick'in Cevabına benzer bir şey kullandım ama iki nokta beni endişelendirdi:

  1. Tam yapılandırma her kullandığımda enjekte ENVediliyor (ki bu büyük yapılandırmalar için kötüdür).
  2. Birden çok giriş noktası tanımlamam gerekiyor çünkü require(env)farklı dosyalara işaret ediyor.

Bulduğum şey, bir yapılandırma nesnesi oluşturan ve onu bir yapılandırma modülüne enjekte eden basit bir besteci.
İşte bunun için kullanıyorum dosya yapısı:

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

main.jsTüm varsayılan yapılandırma şeyler tutar:

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

module.exports = mainConfig;

dev.jsVe production.jsana yapılandırma geçersiz kılar tek tutma yapılandırma şeyler:

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

module.exports = devConfig;

Önemli olan kısım, webpack.config.jsyapılandırmayı oluşturan ve oluşturulan yapılandırma nesnesini tutan bir ortam değişkeni oluşturmak için DefinePlugin'i kullanandır __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))
    })
  ]
};

Son adım şimdi şudur: config.js(Burada es6 içe aktarma dışa aktarma sözdizimini kullanarak web paketi altında):

const config = __APP_CONFIG__;

export default config;

Gözlerinde farklı app.jsartık kullanabilirsiniz import config from './config';yapılandırma nesnesi alır.


2
Burada gerçekten en iyi cevap
Gabriel

18

Başka bir yol da bir JS dosyasını bir olarak kullanmaktır proxyve bu dosyanın ilgilenilen modülü yüklemesine izin verin commonjsve aşağıdaki gibi dışa aktarın 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

O zaman uygulamanızda ES2015 modülünü normal şekilde kullanabilirsiniz:

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

19
İf / else ve require ile ilgili tek sorun, gerekli her iki dosyanın da oluşturulan dosyada paketlenmesidir. Bir çözüm bulamadım. Esasen önce demetleme, sonra parçalama olur.
alex

2
Eğer webpack dosyasında eklenti kullanmak eğer doğru değil necesary var webpack.optimize.UglifyJsPlugin(), WebPack optimizasyonu Şartlı iç hat kodu her zaman yanlıştır şekilde webpack, modülü yüklemek oluşturulan paket çıkarın olmaz
Alejandro Silva

@AlejandroSilva bunun bir repo örneğiniz var mı?
Capuchin

1
evet @thevangelist: github.com/AlejandroSilva/mototracker/blob/master/... öyle bir düğüm + reaksiyona + Redux hayvan PROYECT: P
Alejandro Silva

4

OP ile aynı sorunla karşılaştığımda ve lisanslama nedeniyle, belirli yapılara belirli kodların dahil edilmemesi gerektiğinden, webpack-conditional-loader'ı aşağıdaki gibi benimsedim :

Derleme komutumda, yapıma uygun bir ortam değişkeni ayarlıyorum. Örneğin package.json'daki 'demo':

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

Okuduğum belgelerde eksik olan kafa karıştırıcı bit, env değişkenimin sürece genel olarak webpack.config / demo.js dosyamda enjekte edilmesini sağlayarak bunu derleme işlemi boyunca görünür kılmam gerektiğidir:

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

Bunu yerine getirerek, her şeyi koşullu olarak hariç tutabilir ve ilgili kodun ortaya çıkan JavaScript'ten düzgün bir şekilde çıkarılmasını sağlayabilirim. Örneğin, route.js'de demo içeriği diğer yapıların dışında tutulur, böylece:

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

Bu, webpack 4.29.6 ile çalışır.


1
Orada da github.com/dearrrfish/preprocess-loader daha fazla özelliğe sahip olan
user9385381

1

Web paketi yapılandırmalarımda env ayarı yapmakta zorlandım. Ne genellikle istiyorum içeride ulaşılabilir böylece ayarlanan env etmektir webpack.config.js,postcss.config.js ve giriş noktası uygulamanın kendisi içindeki ( index.jsgenellikle). Umarım bulgularım birine yardımcı olabilir.

Bulduğum çözüm, --env productionveya geçmek --env developmentve sonra içeride modu ayarlamak webpack.config.js. Ancak bu, envistediğim yerde erişilebilir hale getirmeme yardımcı olmuyor (yukarıya bakın), bu yüzden ayrıcaprocess.env.NODE_ENV burada önerildiği gibi açıkça . Sahip olduğum en alakalı kısım webpack.config.jsaşağıda.

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

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


-1

Bu en iyi çözüm olmasa da bazı ihtiyaçlarınız için işe yarayabilir. Bunu kullanarak düğüm ve tarayıcıda farklı kod çalıştırmak istiyorsanız benim için çalıştı:

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

1
OP bir derleme zamanı kararını soruyor, cevabınız çalışma süresi ile ilgili.
Michael
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.