Conditional build based on environment using Webpack

前端 未结 9 652
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-30 21:40

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 an

相关标签:
9条回答
  • 2020-11-30 22:05

    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-loader as follows:

    In my build command I set an environment variable appropriately for my build. For example 'demo' in 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 processing by ensuring my env variable gets injected into the process global thus in my 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:

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

    0 讨论(0)
  • 2020-11-30 22:13

    Not sure why the "webpack.DefinePlugin" answer is the top one everywhere for defining Environment based imports/requires.

    The problem with that approach is that you are still delivering all those modules to the client -> check with webpack-bundle-analyezer for instance. And not reducing your bundle.js's size at all :)

    So what really works well and much more logical is: NormalModuleReplacementPlugin

    So rather than do a on_client conditional require -> just not include not needed files to the bundle in the first place

    Hope that helps

    0 讨论(0)
  • 2020-11-30 22:17

    Use ifdef-loader. In your source files you can do stuff like

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

    The relevant webpack configuration is

    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}`,
            },
          },
        ],
      },
      // ...
    };
    
    0 讨论(0)
  • 2020-11-30 22:20

    I ended up using something similar to Matt Derrick' Answer, but was worried about two points:

    1. The complete config is injected every time I use ENV (Which is bad for large configs).
    2. I have to define multiple entry points because require(env) points to different files.

    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:

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

    The main.js holds all default config stuff:

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

    The dev.js and production.js only hold config stuff which overrides the main config:

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

    The important part is the webpack.config.js which composes the config and uses the DefinePlugin to generate a environment variable __APP_CONFIG__ which holds the composed config object:

    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):

    const config = __APP_CONFIG__;
    
    export default config;
    

    In your app.js you could now use import config from './config'; to get the config object.

    0 讨论(0)
  • 2020-11-30 22:20

    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:

    // 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:

    // myApp.js
    import { myString } from './store/myModule.js'
    myString // <- "this is in dev"
    
    0 讨论(0)
  • 2020-11-30 22:24

    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.js and inside the entry point application itself (index.js usually). I hope that my findings can help someone.

    The solution that I've come up with is to pass in --env production or --env development, and then set mode inside webpack.config.js. However, that doesn't help me with making env accessible where I want it (see above), so I also need to set process.env.NODE_ENV explicitly, as recommended here. Most relevant part that I have in webpack.config.js follow below.

    ...
    module.exports = mode => {
      process.env.NODE_ENV = mode;
    
      if (mode === "production") {
        return merge(commonConfig, productionConfig, { mode });
      }
      return merge(commonConfig, developmentConfig, { mode });
    };
    
    0 讨论(0)
提交回复
热议问题