Javascript 使用 Webpack 基于环境条件构建
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28572380/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Conditional build based on environment using Webpack
提问by Dominic
I have some things for development - e.g mocks which I would like to not bloat my distributed build file with.
我有一些需要开发的东西 - 例如,我不想用它来膨胀我的分布式构建文件的模拟。
In RequireJS you can pass a config in a plugin file and conditonally require things in based on that.
在 RequireJS 中,你可以在插件文件中传递一个配置,并根据它有条件地要求一些东西。
For webpack there doesn't seem to be a way of doing this. Firstly to create a runtime config for an environment I have used resolve.aliasto repoint a require depending on the environment, e.g:
对于 webpack,似乎没有办法做到这一点。首先创建一个环境中运行时配置我用resolve.alias重新指向根据环境,例如要求:
// All settings.
var all = {
fish: 'salmon'
};
// `envsettings` is an alias resolved at build time.
module.exports = Object.assign(all, require('envsettings'));
Then when creating the webpack config I can dynamically assign which file envsettingspoints to (i.e. webpackConfig.resolve.alias.envsettings = './' + env).
然后在创建 webpack 配置时,我可以动态分配envsettings指向哪个文件(即webpackConfig.resolve.alias.envsettings = './' + env)。
However I would like to do something like:
但是我想做一些类似的事情:
if (settings.mock) {
// Short-circuit ajax calls.
// Require in all the mock modules.
}
But obviously I don't want to build in those mock files if the environment isn't mock.
但显然,如果环境不是模拟的,我不想在这些模拟文件中构建。
I could possibly manually repoint all those requires to a stub file using resolve.alias again - but is there a way that feels less hacky?
我可以再次使用 resolve.alias 手动将所有这些要求重新指向存根文件 - 但有没有一种感觉不那么讨厌的方法?
Any ideas how I can do that? Thanks.
任何想法我怎么能做到这一点?谢谢。
采纳答案by Matt Derrick
You can use the define plugin.
您可以使用定义插件。
I use it by doing something as simple as this in your webpack build file where envis the path to a file that exports an object of settings:
我通过在你的 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 };
and then this in your code
然后在你的代码中
if (ENV.debug) {
console.log('Yo!');
}
It will strip this code out of your build file if the condition is false. You can see a working Webpack build example here.
如果条件为假,它将从您的构建文件中删除此代码。您可以在此处查看工作的Webpack 构建示例。
回答by Roman Zhyliov
Not sure why the "webpack.DefinePlugin" answer is the top one everywhere for defining Environment based imports/requires.
不知道为什么“webpack.DefinePlugin”答案是定义基于环境的导入/要求的最重要的答案。
The problemwith that approach is that you are still delivering all those modules to the client -> check with webpack-bundle-analyezerfor instance. And not reducing your bundle.js's size at all :)
这种方法的问题在于您仍然将所有这些模块交付给客户端 ->例如,使用webpack-bundle-analyezer进行检查。并且根本没有减少您的 bundle.js 的大小:)
So what really works well and much more logical is: NormalModuleReplacementPlugin
所以真正有效且更合乎逻辑的是:NormalModuleReplacementPlugin
So rather than do a on_client conditional require -> just not include not needed files to the bundle in the first place
因此,与其执行 on_client 条件要求 -> 只是不首先将不需要的文件包含到包中
Hope that helps
希望有帮助
回答by May Oakes
Use ifdef-loader. In your source files you can do stuff like
使用ifdef-loader. 在您的源文件中,您可以执行以下操作
/// #if ENV === 'production'
console.log('production!');
/// #endif
The relevant webpackconfiguration is
相关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}`,
},
},
],
},
// ...
};
回答by ofhouse
I ended up using something similar to Matt Derrick' Answer, but was worried about two points:
我最终使用了类似于Matt Derrick' Answer 的东西,但担心两点:
- The complete config is injected every time I use
ENV(Which is bad for large configs). - I have to define multiple entry points because
require(env)points to different files.
- 每次使用时都会注入完整的配置
ENV(这对大型配置不利)。 - 我必须定义多个入口点,因为
require(env)指向不同的文件。
What I came up with is a simple composer which builds a config object and injects it to a config module.
Here is the file structure, Iam using for this:
我想出的是一个简单的composer,它构建一个配置对象并将其注入到配置模块中。
这是文件结构,我为此使用:
config/
└── main.js
└── dev.js
└── production.js
src/
└── app.js
└── config.js
└── ...
webpack.config.js
The main.jsholds all default config stuff:
在main.js拥有所有默认配置的东西:
// main.js
const mainConfig = {
apiEndPoint: 'https://api.example.com',
...
}
module.exports = mainConfig;
The dev.jsand production.jsonly hold config stuff which overrides the main config:
在dev.js和production.js唯一保持配置的东西将覆盖主要配置:
// dev.js
const devConfig = {
apiEndPoint: 'http://localhost:4000'
}
module.exports = devConfig;
The important part is the webpack.config.jswhich composes the config and uses the DefinePluginto generate a environment variable __APP_CONFIG__which holds the composed config object:
重要的部分是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))
})
]
};
The last step is now the config.js, it looks like this (Using es6 import export syntax here because its under webpack):
现在最后一步是config.js,它看起来像这样(这里使用 es6 导入导出语法,因为它在 webpack 下):
const config = __APP_CONFIG__;
export default config;
In your app.jsyou could now use import config from './config';to get the config object.
在您app.js现在可以import config from './config';用来获取配置对象。
回答by Alejandro Silva
another way is using a JS file as a proxy, and let that file load the module of interest in commonjs, and export it as es2015 module, like this:
另一种方法是使用 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
Then you can use ES2015 module in your app normally:
然后你就可以在你的应用中正常使用 ES2015 模块了:
// myApp.js
import { myString } from './store/myModule.js'
myString // <- "this is in dev"
回答by Paul Whipp
Faced with the same problem as the OP and required, because of licensing, not to include certain code in certain builds, I adopted the webpack-conditional-loaderas follows:
面对与 OP 相同的问题并要求,由于许可,不能在某些构建中包含某些代码,我采用了webpack-conditional-loader,如下所示:
In my build command I set an environment variable appropriately for my build. For example 'demo' in package.json:
在我的构建命令中,我为我的构建适当地设置了一个环境变量。例如 package.json 中的“演示”:
...
"scripts": {
...
"buildDemo": "./node_modules/.bin/webpack --config webpack.config/demo.js --env.demo --progress --colors",
...
The confusing bit that is missing from the documentation I read is that I have to make this visible throughout the build processingby ensuring my env variable gets injected into the process global thus in my webpack.config/demo.js:
我阅读的文档中缺少的令人困惑的一点是,我必须通过确保我的 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};
With this in place, I can conditionally exclude anything, ensuring that any related code is properly shaken out of the resulting JavaScript. For example in my routes.js the demo content is kept out of other builds thus:
有了这个,我可以有条件地排除任何东西,确保任何相关的代码都被正确地从结果 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
...
This works with webpack 4.29.6.
这适用于 webpack 4.29.6。
回答by Max
I've struggled with setting env in my webpack configs. What I usually want is to set env so that it can be reached inside webpack.config.js, postcss.config.jsand inside the entry point application itself (index.jsusually). I hope that my findings can help someone.
我一直在努力在我的 webpack 配置中设置 env。我通常要的是设置环境,以便它可以在内部达成webpack.config.js,postcss.config.js和入口点应用程序本身内(index.js通常情况下)。我希望我的发现可以帮助某人。
The solution that I've come up with is to pass in --env productionor --env development, and then set mode inside webpack.config.js.
However, that doesn't help me with making envaccessible where I want it (see above), so I also need to set process.env.NODE_ENVexplicitly, as recommended here.
Most relevant part that I have in webpack.config.jsfollow below.
我想出的解决方案是传入--env productionor --env development,然后在里面设置 mode 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 });
};
回答by Simon H
Use envirnment variables to create dev and prod deployments:
使用环境变量创建 dev 和 prod 部署:
回答by Esqarrouth
While this is not the best solution, it may work for some of your needs. If you want to run different code in node and browser using this worked for me:
虽然这不是最好的解决方案,但它可能会满足您的某些需求。如果你想在节点和浏览器中运行不同的代码,这对我有用:
if (typeof window !== 'undefined')
return
}
//run node only code now

