使用Webpack基于环境的条件构建


93

我有一些要开发的东西-例如,我不想让我的分布式构建文件肿的模拟。

在RequireJS中,您可以在插件文件中传递配置,然后根据需要进行配置。

对于webpack,似乎没有办法做到这一点。首先创建一个环境中运行时配置我用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')将指向非模拟环境中的空文件。作品
Dominic 2015年

Answers:


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

如果条件为假,它将将此代码从构建文件中删除。您可以在此处看到一个有效的Webpack构建示例


这个解决方案让我有些困惑。它没有提到我应该如何设置env。查看该示例,似乎他们正在通过gulp和yargs处理该标志,但并不是所有人都在使用。
安德烈

1
这对短绒如何起作用?您是否需要手动定义在Define插件中添加的新全局变量?
2016年

2
@标记是的。添加像"globals": { "ENV": true }你.eslintrc
马特·德里克-

我将如何访问组件中的ENV变量?我尝试了上述解决方案,但仍然收到未定义ENV的错误
jasan

18
它不会从构建文件中剥离代码!我测试了一下,代码在这里。
Lionel

42

不确定为什么“ webpack.DefinePlugin”答案是定义基于环境的导入/需求的最佳方法。

这种方法的问题在于,您仍将所有这些模块交付给客户端-> 例如,使用webpack-bundle-analyezer检查。而且根本不减小bundle.js的大小:)

因此,真正有效且更合乎逻辑的是:NormalModuleReplacementPlugin

因此,与其做一个on_client有条件的需求->只是不首先将不需要的文件不包括到捆绑中

希望有帮助


尼斯不知道那个插件!
多米尼克

在这种情况下,您是否每个环境都没有多个版本?例如,如果我具有用于dev / QA / UAT /生产环境的Web服务地址,则需要4个单独的容器,每个环境1个。理想情况下,您将有一个容器并使用环境变量启动它以指定要加载的配置。
Brett Mathe

不,不是。这就是您使用插件所做的->您可以通过env vars指定环境,并且它仅构建一个容器,但是针对特定环境没有多余的包含物。当然,这还取决于您如何设置Webpack配置,显然您可以构建所有构建,但这不是此插件的用途和作用。
罗曼·兹里约夫

33

使用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 '18 -4-20

1
非常感谢!它像一种魅力。使用ContextReplacementPlugin,NormalModuleReplacementPlugin和其他工具进行的几个小时的实验都失败了。这是ifdef-loader,可以节省我的时间。
jeron-diovis

27

我最终使用了类似于Matt Derrick'Answer的方法,但担心两点:

  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.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,最后一步是,它看起来像这样(在此处使用es6 import export语法,因为它在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的唯一问题是两个必需文件都将被捆绑到生成的文件中。我还没有找到解决方法。本质上,捆绑首先发生,然后进行重整。
alex 2016年

2
那不是必须的,如果您在webpack文件中使用plugin webpack.optimize.UglifyJsPlugin(),则webpack的优化将不会加载该模块,因为条件中的行代码始终为false,因此webpack将从生成的包中删除它
Alejandro Silva

@AlejandroSilva,你有回购的例子吗?
thevangelist

1
@thevangelist是:github.com/AlejandroSilva/mototracker/blob/master/…这是一个节点+反应+ redux宠物对象:P
Alejandro Silva

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

我一直在努力在我的webpack配置中设置env。我通常想要的是设置env,以便可以在webpack.config.jspostcss.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正在询问编译时间决定,您的答案是关于运行时间。
迈克尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.