客户端代码的 Laravel Mix/Webpack 环境因变量 [英] Laravel Mix / Webpack environment dependent variable for client code

查看:35
本文介绍了客户端代码的 Laravel Mix/Webpack 环境因变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm looking for a way to build my assets using Laravel Mix on my dev machine which will then match the parameters on the production.

例如,我的 API 有一个基本 url,它是 http://foo.test/api/v1 用于本地开发和 https://foo.com/api/v1 在我的生产服务器上.

For instance, I have a base url for my API which is http://foo.test/api/v1 for local development and https://foo.com/api/v1 on my production server.

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

So in my client code (e.g. http.js) I would like to do something like this:

Vue.axios.defaults.baseURL = API_BASE_URL;

现在 API_BASE_URL 应该在我运行 npm run devhttp://foo.test/api/v1 时替换为 http://foo.test/api/v1code>https://foo.com/api/v1 当我运行 npm run prod 时.

Now API_BASE_URLshould get replaced with http://foo.test/api/v1 when I run npm run dev or with https://foo.com/api/v1 when I run npm run prod.

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

I already tried the following approach which does not work.

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 尝试了这种方法.

I tried this approach with process.env.API_BASE_URL as well.

推荐答案

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

It's a bit late, and I don't know if it still applies, but we're probably not the only ones looking for this. I've spend a lot of time on this, trying to figuring out that exact question. I'll share my story here, hoping that other people can avoid the loss of time.

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

What is passed to your client side application is basically the contents of process.env. Laravel Mix tries to protect you from exposing the entire .env (that gets loaded via dotenv) by filtering the variables and allowing only those that start with MIX_. This behavior comes from the MixDefinitionsPlugin.

当 webpack 配置被组装时,它首先放置你的自定义插件,然后是混合插件.这意味着 MixDefinitionsPlugin 将始终是最后一个词.

When the webpack configuration is assembled it places your custom plugins first, followed by the mix plugins. That means the MixDefinitionsPlugin will always have the last word.

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

If you can work with that limitation, then a simple solution would be:

在您的 .env 中添加以下内容:

Add the following in your .env:

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

当您重新编译您的资产时,您将在客户端拥有一个 process.env.MIX_API_URL.就这么简单.

When you recompile your assets you'll have a process.env.MIX_API_URL on the client side. It's as simple as that.

顺便说一句,由于这是使用 dotenv-expand 加载的,您甚至可以使用其他变量(即,如果您的 api 已为后端设置):

Btw, since this loads using dotenv-expand you can even use other variables (ie if your api was already set for the backend):

MIX_API_URL=${API_URL}

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

If you want to do this for your production variant, the least amount of hassle would be (temporarily) replace your .env file with a production one. Just create a shell script to build your production variant:

  • 将开发 .env 替换为您的生产 .env.
  • 运行npm run production.
  • 恢复你的开发.env
  • Replace the development .env with your production .env.
  • Run npm run production.
  • Restore your dev .env

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

Done. There is a 2nd solution, it's a lot uglier, but I like it more, read on :)

我对此并不满意,因为我只是希望能够运行 npm run production 并且它可以工作,所以我进一步研究.

I wasn't really happy with that as I just wanted to be able to run npm run production and it would work, so I looked further.

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

Since only the last defined process.env is used when the javascript transpiles, the plugin closest to that wins. As mentioned above, this is the MixDefinitionsPlugin and there is nothing you can do about it.

如果研究其他几种实现我们想要的方法

If looked into several other ways of accomplishing what we want

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

(Besides the script replacing the .env, this is the only viable solution I could find)

Mix 在完成加载后立即发出一个loading-plugins"事件,并将插件作为参数.这意味着您将获得一组准备充分且非常漂亮的插件,这些插件即将加入 webpack.

Mix emits a "loading-plugins" event with the plugins as parameter right after it finalised their loading. This means that you'll get a set of very prepared and very pretty plugins that are about to go to webpack.

一开始我试图在数组的末尾推送一个插件,但那个插件只在转译后执行,所以你什么也得不到.另一种方法是找出插件所在的位置并使用它.如果您可以在那里修改其内容,理论上您可以即时"添加值,这些值也会最终出现在您的浏览器中.这就是我所做的.

Initially I tried to push a plugin at the end of the array, but that one is only executed after the transpilation so that gains you nothing. Alternative would be to figure out where the plugin is situated and work with that. If you can modify its contents there you could in theory add values "on the fly" and those will end up in your browser as well. Which is what I did.

基本上归结为以下几点:

Basically this boils down to the following:

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,因为它已经完成了它的工作)

(Note that we're looking for a DefinePlugin here instead of the MixDefinitionsPlugin, as that already did its work)

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

Since you can add what you need here without the need for the MIX_-prefix, you could alternatively set your API_URL here for both:

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())并将其合并到定义中,无限可能.

You could also load another .env file here (via dotenv.config()) and merge it into the definitions, the sky is the limit.

我想到的其他事情.

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

The MixDefinitionsPlugin constructor allows for this possibility (function MixDefinitionsPlugin(envPath)), unfortunately the actual implementation does not:

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

所以没有这种运气,这是无法使用的.

So no such luck, this is unusable.

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

Next up was checking if I could perhaps create a different folder with a production .env file.

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

Mix has a Paths object that stores the root. So the idea here was to store my alternative .env in a subdirectory, update the root and presto.

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

这适用于 .env,但它破坏了很多其他的东西.根用作 webpack 的上下文,如purify-css 插件的配置(vue 和刀片文件的 css 清理),最重要的是它还破坏了 url 加载器.这里也不要去(除非你想拦截webpack配置,到处恢复root,不值得)

This works for the .env, but it breaks a lot of other things. The root is used as the context for webpack, as configuration for the purify-css plugin (css cleaning of vue and blade files) and most importantly it also breaks the url loader. No go here as well (unless you want to intercept the webpack configuration and restore the root everywhere, not worth it)

到此结束我生命中大约 5 个小时的故事.我远不是 javascript 专家,所以也许我犯了一些错误或遗漏了一些东西,但这是我的故事:)

So here ends the story of about 5 hours of my life. I'm nowhere near a javascript expert, so perhaps I've made som errors or missed things, but it's my story :)

这篇关于客户端代码的 Laravel Mix/Webpack 环境因变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆