Vue CLI - Keep config file as external after compilation
Asked Answered
C

4

11

I'm developing an application with Vue CLI. This application is a web interface which will have to communicate with a Rest API on a board.

So, because the board will move, the IP of the board will change over time depending on where I am.

This is my project current tree :

Project tree

The IP configuration is contained in the Settings.js file :

export const Settings = {
    // Server configuration
    SERVER_IP: '127.0.0.1',
    SERVER_PORT: '9000',

    SERVER_PROTOCOL: 'http', // http or https

    // Website configuration
    DEBUG_MODE: true
};

And in my files, I import this IP with the following statement :

import {Settings} from '../../Settings'
const ip = Settings.SERVER_IP;

// Do stuff

This works fine. But the problem is: I have to recompile everything when the IP change. Because Settings.js is compiled with other JS files.

So, I would like to know if there is a way to have a config file which will remain in the dist/ directory and will be read by my JS application during execution. So I will not have to recompile everything each time my application server IP change.

Ty for your help :)

Carlicarlick answered 2/1, 2019 at 10:33 Comment(0)
C
12

My solution for Vue is based on the solution for Angular.

You can have environment variables exactly like backend developers use.

But the difference is that backend code is executed inside the server while frontend code is nothing but static files on the disk that you withdraw as is without giving them even a chance to run and check env vars before being delivered to a browser.

However, your code has to be executed inside a browser itself. So this is the ideal and the only proper place to have an env. Thus, you have to prepare this env in advance - according to your backend env.

Here is the plan:

  1. You exclude from compilation your settings file (see below).
  2. Your settings file "constructs" the env before you run the Vue app.
  3. From your code you use that env and, also, you can update this env in runtime.

So here is your final code structure:

root_project_dir:
├─> cfg
│   └── settings.js
├─> public
│   ├── favicon.ico
│   └── index.html
├─> src
│   ├── App.vue
│   ├─> assets
│   │   └── logo.png
│   ├─> components
│   ├─> layouts
│   ├── main.js
│   ├─> plugins
│   ├─> router
│   ├─> store
│   └─> views
└── vue.config.js

Create settings file cfg/settings.js:

/*
This file goes as an asset without any of compilation even after build process.
Thus, it can be replaced in a runtime by different file in another environment.

Example for Docker:
  docker run -v ./local_cfg_dir:cfg image:tag
*/

(function(window) {
  window.__env = window.__env || {};

  window.__env.api = {
    "url": "http://127.0.0.1:8000",
    "timeout": 80000
  };
  window.__env.captcha = {
    "enabled": true,
    "key": "Mee1ieth1IeR8aezeiwi0cai8quahy"
  };
  window.__env.rollbar = {
    "enabled": true,
    "token": "zieriu1Saip5Soiquie6zoo7shae0o"
  };
  window.__env.debug = true;
})(this);

Provide Webpack Copy plugin with the instruction to copy cfg files during npm run build stage in vue.config.js (you can't change this name):

module.exports = {
  chainWebpack: config => {
    config.plugin("copy").tap(([pathConfigs]) => {
      pathConfigs.unshift({
        from: "cfg",
        to: "cfg"
      });
      return [pathConfigs]})
  },
  transpileDependencies: ["vuetify"]
};

Check the resultant webpack config and find it was applied (at the bottom of the output):

vue inspect

Now, when you build the project you will see it in the resultant dir:

dist
 ├─> cfg
 │   └── settings.js
 ├─> css
 │   ├── app.06b1fea6.css
 │   └── chunk-1f2efba6.a298b841.css
 ├── favicon.ico
 ├─> img
 │   └── logo.09e0e4e1.png
 ├── index.html
 └─> js
     ├── app.8fc75c19.js
     ├── app.8fc75c19.js.map
     └── chunk-vendors.1ab49693.js.map

So you can run this setup in public/index.html before you run app in the same window:

  <body>
    <script src="/cfg/settings.js"></script>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>

Now you have it in your window:

enter image description here

And from any place in the code you can reach this env:

Vue.use(VueReCaptcha, { siteKey: window.__env.captcha.key })

Sidenote:

If you want to be "DevOps-compatible" you need to have a settings in a nested directory (cfg in our example). This will give an ability to make mounts in Kubernetes/Swarm without overwriting entire directory of dist.

Classical answered 3/1, 2020 at 10:3 Comment(5)
Thanks for a good guide. I'm not getting it to work properly though, as the final step "from any place in the code you can reach this env" doesn't work. I'm getting an error in VSCode that "Property '__env' does not exist on type 'Window & typeof globalThis'." The app I'm developing is in typescript.Radke
@Radke but does it really exist? Can you see window.__env in the console just like on the picture above?Classical
yes, I can see window.__env in the console. Could it be that this solution doesn't play well with typescript? I have found another solution though using parts of your solution. Will post in a separate answer.Radke
@Radke if your window.__env exists and contains the data from settings.js - solution works 100%. If your code can't reach child object of window - this is separate problem and deserves a dedicated Question on StackOveflow. Maybe you need to declare type fisrt: (window as any).__envClassical
Excellent explanation. Note on Vue 3 - Vue.use is no longer available; I used the provide/inject pattern described here: https://mcmap.net/q/240878/-add-global-variable-in-vue-js-3Zoril
M
5

you can create a config.json file in public directory and then in main js you can fetch it and load the config file. Now your dist created with this config.json.

fetch('/config.json').then(res => res.json()).then(config => {
     .......
})
Maupassant answered 8/11, 2019 at 7:36 Comment(1)
Very simple and useful, thank you so much!Bebebebeerine
D
1

Using Melih Altıntaş's answer (thank you!! :) ) but wanting to avoid fetching, I created a JavaScript file in /public folder:

/public/config.js --> /dist/config.js

loaded it in index.html head

<script src="<%= BASE_URL %>config.js">

and used variables declared in /public/config.js in any subsequent *.js file I needed (for example main.js, mixins.js...).

After the build, /dist/config.js can be easily modified, and it's values still automatically used in the app without a hitch (and recompiling ;) ).

Maybe it's a stupid solution, but it works. 😂 Did exactly what I needed it to do (have client easily edit certain values).

Thank you once more, Melih! :)

Dionedionis answered 22/1, 2022 at 16:30 Comment(0)
H
0

Do you know the complete list of IP addresses in advance? If so you can create a function that returns the correct IP based on whatever time/location logic.

Otherwise you can try moving the settings file to the public folder, add to your .gitignore file, making sure you reference it in your public/index.html. It will now sit outside of the compiled Vue app and can be accessed from within Vue as a global settings variable.

So instead of import {Settings} from '../../Settings' you would reference window.Settings

e.g. const ip = window.Settings.SERVER_IP;

This way you can edit the settings directly without having to recompile each time.

Hartill answered 3/1, 2019 at 16:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.