Webpack을 사용하는 환경 기반 조건부 빌드


95

개발을위한 몇 가지 사항이 있습니다. 예를 들어 배포 된 빌드 파일을 부 풀리지 않게하고 싶은 모의 작업이 있습니다.

RequireJS에서는 플러그인 파일에 구성을 전달할 수 있으며이를 기반으로 조건부로 요구할 수 있습니다.

웹팩의 경우이 작업을 수행하는 방법이없는 것 같습니다. 먼저 환경에 대한 런타임 구성을 만들기 위해 resolve.alias 를 사용 하여 환경에 따라 요구 사항 을 다시 지정했습니다. 예 :

// 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')는 모의가 아닌 환경에서 빈 파일을 가리킬 것입니다. 작동합니다.
도미닉

답변:


60

define 플러그인을 사용할 수 있습니다 .

env설정 개체를 내보내는 파일의 경로 인 webpack 빌드 파일에서 다음과 같이 간단한 작업을 수행하여 사용합니다 .

// 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!');
}

조건이 거짓이면 빌드 파일에서이 코드를 제거합니다. 여기 에서 작동하는 Webpack 빌드 예제를 볼 수 있습니다 .


이 솔루션에 약간 혼란 스럽습니다. 내가 어떻게 설정해야하는지 언급하지 않습니다 env. 이 예제를 살펴보면 모두가 사용하지 않는 gulp 및 yargs를 통해 해당 플래그를 처리하는 것처럼 보입니다.
앙드레

1
이것은 린터와 어떻게 작동합니까? 플러그인 정의에 추가 된 새 전역 변수를 수동으로 정의해야합니까?
마크

2
@ 표시 예. "globals": { "ENV": true }.eslintrc에 비슷한 것을 추가하십시오
Matt Derrick

컴포넌트의 ENV 변수에 어떻게 액세스합니까? 위의 솔루션을 시도했지만 여전히 ENV가 정의되지 않았다는 오류가 발생합니다.
jasan

20
빌드 파일에서 코드를 제거하지 않습니다! 나는 그것을 테스트했고 코드는 여기에 있습니다.
라이오넬

42

"webpack.DefinePlugin"답변이 환경 기반 가져 오기 / 요구 사항을 정의하는 모든 곳에서 가장 높은 이유를 잘 모르겠습니다.

이 접근 방식 의 문제점 은 여전히 ​​모든 모듈을 클라이언트에 제공하고 있다는 것 입니다. 예를 들어 webpack-bundle-analyezer 로 확인하십시오 . 그리고 bundle.js의 크기를 전혀 줄이지 않습니다. :)

따라서 실제로 잘 작동하고 훨씬 더 논리적 인 것은 NormalModuleReplacementPlugin입니다.

따라서 on_client 조건부 요구 사항을 수행하는 대신-> 처음에 번들에 필요하지 않은 파일을 포함하지 마십시오.

도움이되는 희망


Nice는 그 플러그인에 대해 몰랐습니다!
Dominic

이 시나리오에서는 환경 당 여러 빌드가 있지 않습니까? 예를 들어 개발 / QA / UAT / 프로덕션 환경에 대한 웹 서비스 주소가있는 경우 각 환경에 대해 하나씩 4 개의 개별 컨테이너가 필요합니다. 이상적으로는 컨테이너가 하나 있고로드 할 구성을 지정하는 환경 변수를 사용하여 시작하는 것입니다.
Brett Mathe 2017 년

아니 정말. 이것이 바로 플러그인으로 수행하는 작업입니다-> env vars를 통해 환경을 지정하고 하나의 컨테이너 만 빌드하지만 중복 포함이없는 특정 환경을위한 것입니다. 물론 웹팩 구성을 설정하는 방법에 따라 다르며 분명히 모든 빌드를 빌드 할 수 있지만이 플러그인이 무엇을 의미하고 수행하는지는 아닙니다.
Roman Zhyliov

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

3
수락 된 답변이 예상대로 코드를 제거하지 않고 전처리 기와 같은 구문이 조건부 요소로 식별 될 가능성이 높기 때문에이 답변을 찬성했습니다.
Christian Ivicevic

1
정말 고마워! 매력처럼 작동합니다. ContextReplacementPlugin, NormalModuleReplacementPlugin 및 기타 항목을 사용한 몇 시간의 실험-모두 실패했습니다. 그리고 여기에 ifdef-loader가 있습니다.
jeron-diovis

28

나는 Matt Derrick 'Answer 와 비슷한 것을 사용 했지만 두 가지 점에 대해 걱정했습니다.

  1. 전체 구성은 내가 사용할 때마다 주입됩니다 ENV(대규모 구성에는 좋지 않음).
  2. require(env)다른 파일을 가리 키기 때문에 여러 진입 점을 정의해야 합니다.

내가 생각 해낸 것은 구성 개체를 만들고 구성 모듈에 주입하는 간단한 작곡가입니다.
파일 구조는 다음과 같습니다.

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.jsproduction.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이며 다음과 같습니다 (webpack 아래에 있으므로 여기에서 es6 가져 오기 내보내기 구문 사용).

const config = __APP_CONFIG__;

export default config;

당신에 app.js당신은 지금 사용할 수 import config from './config';는 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의 유일한 문제는 두 필수 파일이 생성 된 파일에 번들로 제공된다는 것입니다. 해결 방법을 찾지 못했습니다. 기본적으로 번들링이 먼저 일어난 다음 맹 글링이 발생합니다.
alex

2
그럴 필요는 없습니다. webpack 파일에서 plugin webpack.optimize.UglifyJsPlugin()을 사용하는 경우 조건부 내부의 라인 코드가 항상 false이므로 webpack 최적화는 모듈을로드하지 않습니다. 따라서 webpack은 생성 된 번들에서 제거합니다.
Alejandro Silva

@AlejandroSilva 당신은 이것의 repo 예가 있습니까?
Capuchin

1
@thevangelist yep : github.com/AlejandroSilva/mototracker/blob/master/… 그것은 node + react + redux pet proyect입니다 : 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};

이를 통해 조건부로 모든 것을 제외하여 관련 코드가 결과 자바 스크립트에서 적절하게 제거되도록 할 수 있습니다. 예를 들어, 내 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
...

이것은 웹팩 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는 컴파일 시간 결정에 대해 묻고 있으며 답변은 런타임에 관한 것입니다.
Michael
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.