web-dev-qa-db-fra.com

Génération conditionnelle basée sur un environnement utilisant Webpack

J'ai certaines choses à développer - par exemple des mocs avec lesquels j'aimerais ne pas gonfler mon fichier de construction distribué.

Dans RequireJS, vous pouvez passer une configuration dans un fichier de plug-in et, de manière conditionnelle, exiger des choses basées sur cela.

Pour WebPack, cela ne semble pas être une façon de faire. Tout d’abord, pour créer une configuration d’exécution pour un environnement que j’ai utilisé resol.alias , pour republier une exigence en fonction de l’environnement, par exemple:

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

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

Ensuite, lors de la création de la configuration webpack, je peux attribuer dynamiquement le fichier vers lequel pointe envsettings (c.-à-d. webpackConfig.resolve.alias.envsettings = './' + env).

Cependant, j'aimerais faire quelque chose comme:

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

Mais évidemment, je ne veux pas intégrer ces fichiers fictifs si l'environnement n'est pas fictif.

Je pourrais éventuellement redéfinir manuellement tous ces éléments nécessaires sur un fichier de raccord en utilisant resol.alias à nouveau - mais y a-t-il un moyen de se sentir moins hacky?

Des idées comment je peux faire ça? Merci.

79
Dominic

Vous pouvez utiliser le define plugin .

Je l'utilise en faisant quelque chose d'aussi simple que cela dans votre fichier de compilation webpack où env est le chemin d'accès à un fichier qui exporte un objet de paramètres:

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

et puis cela dans votre code

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

Il supprimera ce code de votre fichier de construction si la condition est fausse. Vous pouvez voir un travail exemple de construction Webpack ici .

54
Matt Derrick

Vous ne savez pas pourquoi la réponse "webpack.DefinePlugin" est la meilleure pour la définition des importations/exigences basées sur l'environnement.

Le problème avec cette approche est que vous livrez toujours tous ces modules au client -> vérifier avec webpack-bundle-analyezer = par exemple. Et ne pas réduire la taille de votre bundle.js :)

Donc, ce qui fonctionne vraiment bien et de manière beaucoup plus logique est: NormalModuleReplacementPlugin

Donc, plutôt que de faire un on_client conditionnel exiger -> tout simplement pas inclure les fichiers non nécessaires à la bundle en premier lieu

J'espère que ça t'as aidé

33
Roman Zhyliov

Utilisation ifdef-loader . Dans vos fichiers source, vous pouvez faire des choses comme

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

La configuration appropriée de webpack est

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

J'ai fini par utiliser quelque chose de similaire à Réponse de Matt Derrick , mais je craignais deux points:

  1. La configuration complète est injectée chaque fois que j'utilise ENV (ce qui est mauvais pour les grandes configurations).
  2. Je dois définir plusieurs points d'entrée parce que require(env) pointe sur des fichiers différents.

Ce que je propose est un simple composer) qui construit un objet de configuration et l'injecte dans un module de configuration.
Voici la structure de fichier qu'Iam utilise pour cela:

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

Le main.js contient tous les éléments de configuration par défaut:

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

module.exports = mainConfig;

Le dev.js et production.js ne contient que des éléments de configuration qui remplacent la configuration principale:

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

module.exports = devConfig;

La partie importante est le webpack.config.js qui compose la configuration et utilise le DefinePlugin pour générer une variable d’environnement __APP_CONFIG__ qui contient l’objet config composé:

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))
    })
  ]
};

La dernière étape est maintenant le config.js, cela ressemble à ceci (Utilisation de la syntaxe importation importation es6 ici car elle se trouve sous Webpack):

const config = __APP_CONFIG__;

export default config;

Dans votre app.js vous pouvez maintenant utiliser import config from './config'; pour obtenir l'objet config.

21
ofhouse

une autre méthode consiste à utiliser un fichier JS en tant que proxy et à laisser ce fichier charger le module qui vous intéresse dans commonjs et à l'exporter en tant que es2015 module, comme ça:

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

Ensuite, vous pouvez utiliser le module ES2015 dans votre application normalement:

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

Confronté au même problème que le PO et obligé, pour des raisons de licence, à ne pas inclure certains codes dans certaines versions, j’ai adopté le webpack-conditionitional-loader comme suit:

Dans ma commande de construction, je définis une variable d'environnement appropriée pour ma construction. Par exemple 'demo' dans package.json:

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

Le peu de confusion qui manque dans la documentation que j'ai lue est que je dois rendre cela visible tout au long du traitement de la construction en veillant à ce que ma variable env soit injectée dans le fichier. processus global ainsi dans mon 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};

Cela mis en place, je peux sous condition exclure quoi que ce soit, en veillant à ce que tout code associé soit correctement évincé du code JavaScript résultant. Par exemple, dans le fichier routes.js, le contenu de la démo est conservé en dehors des versions suivantes:

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

Cela fonctionne avec webpack 4.29.6.

3
Paul Whipp

J'ai eu du mal à définir env dans mes configs de webpack. Ce que je veux généralement, c’est de régler env pour pouvoir l’atteindre à l'intérieur de webpack.config.js, postcss.config.js et à l'intérieur de l'application de point d'entrée elle-même (index.js généralement). J'espère que mes découvertes peuvent aider quelqu'un.

La solution que j’ai trouvée est de passer en --env production ou --env development, puis définissez le mode dans webpack.config.js. Cependant, cela ne m'aide pas à rendre env accessible où je le veux (voir ci-dessus), je dois donc aussi définir process.env.NODE_ENV explicitement, comme recommandé ici . La partie la plus pertinente que j'ai dans webpack.config.js Suivre ci-dessous.

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

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

Bien que ce ne soit pas la meilleure solution, cela peut répondre à certains de vos besoins. Si vous souhaitez exécuter un code différent dans le noeud et le navigateur à l'aide de cette méthode a fonctionné pour moi:

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

Utilisez des variables d'environnement pour créer des déploiements de dev et de prod:

https://webpack.js.org/guides/environment-variables/

0
Simon H