如何在编译时使用 webpack 替换文件?

Posted

技术标签:

【中文标题】如何在编译时使用 webpack 替换文件?【英文标题】:How can I replace files at compile time using webpack? 【发布时间】:2018-12-22 15:05:35 【问题描述】:

我有一个具有多种配置的项目。第一个配置是config.dev.js 文件,其中包含一些开发配置。我在开发模式下使用它。第二个配置是config.js 文件。我在生产模式下使用它。

在我使用导入的代码中:

import * as config from './config.js';

我想在开发中使用第一个配置文件,在生产中使用第二个配置文件而不重写所有导入。如何根据构建模式替换此配置?

【问题讨论】:

【参考方案1】:

另一种方法是使用 Webpack.DefinePlugin。如果您想要有条件地包含一个很小的代码块,则特别有用。

示例如下:

// webpack.conf.js
// Code block that should be inserted or left blank
const ServiceWorkerReg = `window.addEventListener('load', () => 
    navigator.serviceWorker.register('service-worker.js').then(registration => 
        console.log('SW registered: ', registration);
    ).catch(registrationError => 
        console.log('SW registration failed: ', registrationError);
    );
);`


appConfig = 
    plugins: [
        new webpack.DefinePlugin(
            __SERVICE_WORKER_REG: isDev ? '' : ServiceWorkerReg,
        ),
    ]
    ...




 // Some module index.js

 __SERVICE_WORKER_REG

 ... // other non conditional code

【讨论】:

【参考方案2】:

这是一个老问题,但我最近偶然发现了同样的问题,webpack.NormalModuleReplacementPlugin 似乎不再起作用(或者至少在我使用 JSON 文件作为配置的情况下不起作用)。相反,我找到了另一个使用别名的解决方案:

const path = require("path");

modules.export = 
    ...
    resolve: 
        ...
        alias: 
            [path.resolve(__dirname, "src/conf/config.json")]:
                path.resolve(__dirname, "src/conf/config.prod.json")
        
    
    ...

【讨论】:

NormalModuleReplacementPlugin 在我的使用过程中也不起作用,同时它错误“NormalModuleFactory.beforeResolve 不再是瀑布挂钩,而是一个吊钩。”似乎与 webpack 版本变化有关:github.com/webpack/webpack/issues/11647【参考方案3】:

我想模仿 Angular 的 fileReplacements 语法,所以我使用了类似 Angular 的 config.json,如果配置键与我传递给 webpack 的 env 匹配,则循环替换并创建几个 Webpack 模块规则。

这不是有史以来最优雅的东西,但这是我最终得到的:

// config.json


    "title": "Some Title",
    "configurations": 
        "production": 
            "fileReplacements": [
                
                    "replace": "src/environments/environment.ts",
                    "with": "src/environments/environment.prod.ts"
                
            ]
        ,
        "lan": 
            "fileReplacements": [
                
                    "replace": "src/environments/environment.ts",
                    "with": "src/environments/environment.lan.ts"
                
            ]
        
    


// webpack.config.js

const appConfig = require('./config.json');



module.exports = (env, argv) => 

    // env.build is like `ng build --prod` as `webpack --env.build=production` 
    const configuration = appConfig.configurations[env.build];
    const fileReplacements = [];

    // Safety Check
    if(configuration && configuration.fileReplacements) 

        // Iterate through replacements
        for(const replacement of configuration.fileReplacements) 
            // create Webpack module rule
            const replace = 
                test: path.resolve(replacement.replace),
                loader: 'file-replace-loader',
                options: 
                    replacement: path.resolve(replacement.with),
                    async: true
                
            
            fileReplacements.push(replace);
        
    

    return 
        mode: //...
        entry: //...
        module: 
            rules: [
                
                    //...
                ,
                // Unpack anywhere here
                ...fileReplacements
            ]
       
    


这样你就不必一直搞乱 webpack 和 regex 测试,只需添加到 config.json 中的数组中

【讨论】:

这基本上也是我想做的。喜欢你的解决方案。不敢相信每个框架都不包含这样的东西。如果没有它,你会想要构建一个应用程序——或者在本地调试一个生产错误吗?【参考方案4】:

你也可以像这样使用babel-loader

//webpack.config.js

const resolve = require('path').resolve;

module.exports = 
  //...
  module: 
    strictExportPresence: true,
    rules: [
      test: /\.config\.js$/,
      include: paths.appSrc,
      loader: require.resolve('babel-loader'),
      options: 
        plugins: [
            [
              "module-resolver",
              
                resolvePath(sourcePath, currentFile, opts) 
                  if(process.env.NODE_ENV === 'development') 
                    return sourcePath.substr(0, sourcePath.lastIndexOf('/')) + '/config.dev.js';
                   else 
                    return sourcePath;
                  
                
              
            ]
          ]
        
    ]
  

这样您甚至可以定义复杂的算法来确定您要使用的文件。

【讨论】:

【参考方案5】:

我知道这是一篇旧帖子,但这是 Google 上的首批结果之一,所以我认为更好的答案会更好。

Webpack 有一个内置的“Normal Module Replacement Plugin”。

plugins: [
  new webpack.NormalModuleReplacementPlugin(
    /some\/path\/config\.development\.js/,
    './config.production.js'
  )
]

为了我的使用,我把 env 文件放在了一个变量下面是一个例子:

let envFilePath = './environment.dev.js';
if (env === 'production') 
  envFilePath = './environment.prod.js';
 else if (env === 'staging') 
  envFilePath = './environment.stg.js';


module.exports = 
    // other webpack stuff
    ....
    plugins:[ 
        new webpack.NormalModuleReplacementPlugin(
            /src\/environment\/environment\.js/,
            envFilePath
        ),
        ...
    ]

【讨论】:

也许值得一提的是,第二条路径是相对于第一条的,意思是:如果我的旧路径在./src/config/dev.js 中,那么./prod.js 将引用./src/config/prod.js,而不是root/prod.js 【参考方案6】:

你可以使用 webpack 文件替换加载器

https://www.npmjs.com/package/file-replace-loader

例子:

//webpack.config.js

const resolve = require('path').resolve;

module.exports = 
  //...
  module: 
    rules: [
      test: /\.config\.js$/,
      loader: 'file-replace-loader',
      options: 
        condition: process.env.NODE_ENV === 'development',
        replacement: resolve('./config.dev.js'),
        async: true,
      
    ]
  

【讨论】:

以上是关于如何在编译时使用 webpack 替换文件?的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js / webpack:当热重载编译它们时,如何清除旧的包 main-*.js 文件?

通过webpack生产模式编译时如何忽略打字稿错误

如何在为服务器开发但不重新编译 webpack 客户端配置时使用 nodemon?

编译 JS 文件时的 Webpack 问题

我可以在构建时选择哪些文件将在 webpack 或 vueloader 中编译吗?

当 javascript 文件夹外的文件更改时触发 webpacker 编译