用于客户端代码的Laravel Mix / Webpack环境因变量

时间:2018-02-21 12:51:54

标签: webpack laravel-mix

我正在寻找一种方法来在我的开发机器上使用Laravel Mix构建我的资产,然后匹配生产中的参数。

例如,我的API有一个基本网址,本地开发为http://foo.test/api/v1,生产服务器为https://foo.com/api/v1

所以在我的客户端代码中(例如http.js)我想做这样的事情:

Vue.axios.defaults.baseURL = API_BASE_URL;

当我运行API_BASE_URLhttp://foo.test/api/v1运行npm run dev时,https://foo.com/api/v1应该被npm run prod替换。

我已经尝试了以下不起作用的方法。

webpack.mix.js

mix.webpackConfig(webpack => {
   return {
      plugins: [
        new webpack.DefinePlugin({
            API_BASE_URL: JSON.stringify('https://foo.com/api/v1'),
        })
      ]
   }
});

然后在 http.js

Vue.axios.defaults.baseURL = API_BASE_URL;

这编译为:

Object({"NODE_ENV":"production"}).API_BASE_URL;

我也尝试使用此process.env.API_BASE_URL方法。

1 个答案:

答案 0 :(得分:7)

有点晚了,我不知道它是否仍然适用,但我们可能不是唯一想要这个的人。 我花了很多时间在这上面,试图找出确切的问题。我将在这里分享我的故事,希望其他人可以避免浪费时间。

传递给客户端应用程序的内容基本上是process.env的内容。 Laravel Mix试图保护您免于暴露整个.env(通过dotenv加载) 过滤变量并仅允许以MIX_开头的变量。 此行为来自MixDefinitionsPlugin

组装webpack配置时,首先放置自定义插件,然后放置混合插件。 这意味着MixDefinitionsPlugin将始终拥有最后一个字。

如果您可以使用该限制,那么一个简单的解决方案就是:

.env中添加以下内容:

MIX_API_URL=http://foo.test/api/v1

当您重新编译资产时,您将在客户端获得process.env.MIX_API_URL。 就这么简单。

顺便说一下,因为使用dotenv-expand加载你甚至可以使用其他变量(例如,如果你的api已经设置为后端):

MIX_API_URL=${API_URL}

如果您想为生产变体执行此操作,则最少量的麻烦(暂时)将您的.env文件替换为生产文件。 只需创建一个shell脚本来构建生产变体:

  • 将开发.env替换为您的作品.env
  • 运行npm run production
  • 恢复您的开发.env

完成。还有第二个解决方案,它更加丑陋,但我更喜欢它,请继续阅读:)

还在吗?

我对此并不满意,因为我只想运行npm run production并且它会起作用,所以我看得更远。

由于在javascript转换时仅使用最后定义的process.env,因此最接近该插件的插件获胜。 如上所述,这是MixDefinitionsPlugin,你无能为力。

如果考虑其他几种方法来完成我们想要的事情

在 MixDefinitionsPlugin之后添加自己的

(除了替换.env的脚本,这是我能找到的唯一可行解决方案)

Mix在完成加载后立即以插件作为参数发出“loading-plugins”事件。 这意味着您将获得一组非常准备且非常漂亮的插件,这些插件即将发送到webpack。

最初我试图在数组的末尾推送一个插件,但是那个只在转换后执行,这样就什么也得不到了。 另一种方法是找出插件所在的位置并使用它。 如果您可以在那里修改其内容,理论上可以“动态”添加值,这些也将在您的浏览器中结束。 这就是我做的。

基本上归结为以下几点:

if (mix.inProduction()) {
  Mix.listen('loading-plugins', plugins => {
    const _ = require('lodash');
    let define = _.find(plugins, plugin => {return plugin.constructor.name === 'DefinePlugin'});
    define.definitions['process.env'].MIX_API_URL = 'http://foo.com/api/v1';
  });
}

(注意我们在这里寻找一个DefinePlugin而不是MixDefinitionsPlugin,因为它已经完成了它的工作)

由于您可以在此处添加所需内容而无需使用MIX_ - 前缀,因此您也可以在此设置API_URL:

Mix.listen('loading-plugins', plugins => {
  const _ = require('lodash');
  let define = _.find(plugins, plugin => {return plugin.constructor.name === 'DefinePlugin'});
  define.definitions['process.env'].API_URL = mix.inProduction() ?
    'http://foo.com/api/v1' : 'http://foo.test/api/v1';
});    

您还可以在此处加载另一个.env文件(通过dotenv.config())并将其合并到定义中,天空是限制。

.env的其他名称?

我想到的其他事情。

MixDefinitionsPlugin构造函数允许这种可能性(function MixDefinitionsPlugin(envPath)),遗憾的是实际实现没有:

return new webpack.DefinePlugin(
  new MixDefinitionsPlugin().getDefinitions(merge)
);

所以没有这样的运气,这是无法使用的。

不同的文件夹?

接下来检查我是否可以使用生产.env文件创建不同的文件夹。

Mix有一个存储根的Paths对象。所以这里的想法是存储我的替代.env 在子目录中,更新root和presto。

Mix.paths.setRootPath(Mix.paths.root('./resources/assets/production'));

这适用于.env,但它打破了很多其他事情。根用作webpack的上下文,如 purify-css插件的配置(css清理vue和刀片文件),最重要的是它还会破坏url加载器。 不要去这里(除非你想拦截webpack配置并在任何地方恢复root,不值得)

所以这里结束了我生命中大约5个小时的故事。 我远远不是一个javascript专家,所以也许我已经犯了错误或错过了东西,但这是我的故事:)