An extremely cute and charming sketch portrait of me!

Renée Blackburn

Software Engineer in Toronto, Canada

renvrant.com

Custom Build Modes with Vue CLI 3

04/24/20195 Min Read — In Vue

Vue CLI is an excellent tool for building, scaffolding and otherwise not-worrying-about setting up your Vue application. Given its official support, it's the standard way to spin up a new Vue application. One of the central selling points of Vue CLI is that it takes care of a lot of the configuration for you so that you don't have to manage fiddly webpack settings just to get your project up and running.

However, sometimes you want to be able to fine-tune the configuration of your application. Such was the case on a recent Vue project here at Rangle, where we needed to be able to push our Vue application to numerous different environments, with different configurations and different features turned on or off. Vue CLI gives developers plenty of flexibility to be able to do something like this, but it can be tricky to identify everywhere that changes are necessary.

In this post, we'll go over how to add and configure a custom build mode other than the default production, development and test modes.

Vue CLI Service Build Modes

Vue CLI was built with the understanding that sometimes the same application will need to run in various modes. This concept has been built directly into the CLI service. Whenever you run vue-cli-service build, you're also able to pass in an additional flag, --mode. By default, Vue CLI has 3 different "pre-configured" modes:

-- development\ -- production

-- test

The default mode for when you serve your project using vue-cli-service serve is development, whereas the default mode for building your project using vue-cli-service build is production. You can append the --mode flag to your serve or build commands to observe the differences in these modes yourself.

Adding a New Mode

Although only those three modes have built-in meaning to the CLI, you can still create your own build modes for your application. We'll call these custom build modes. Your custom build mode can have any name. In our case, we want to create a build mode for staging. To build our app in staging mode, we'd run the following command:

$ vue-cli-service build --mode staging\

When we run this command, the Vue application will be built in "staging" mode. Without doing any other configuration, this doesn't mean much. If you ran this in a project without changing anything else, you'd find that the resulting build would be identical to running the build process in development mode.

Nevertheless, modes are an important concept in Vue CLI because switching modes is what allows the CLI to load different sets of environment variables. Once we know this, we can use environment variables in order to fine-tune the differences between our application modes.

One important thing to note is that setting the build mode for the CLI service isn't the same as setting the webpack build mode. The webpack build mode is controlled by an environment variable called NODE_ENV and changes how the app is built - for instance, whether or not your code will be minified. Skip ahead to the Configuring Webpack section for more information on this.

Environment Variables

Vue CLI keeps track of the mode you have instructed it to build your project in. As a part of the build process, it will look for a file in the project root called .env.[BUILD-MODE-NAME] and load in only the file that matches. For example, in production, .env.production will be loaded. By matching the name of a .env file to the name of our build mode, we can load in variables for our custom build mode. In the event that this file does not exist, the CLI will default to looking for an .env file and use that for all environment variables.

Inside of the .env file you can declare or overwrite variables that will be available on the global process.env object when the app builds. These variable declarations shouldn't use JavaScript and can't be dynamic. You can think of these variables as application constants and use them to store information that differs between environments. In our application, we used environment variables to manage:

  • The name of the current build mode*

  • The root URL of API services

  • The root path of the router

  • API keys that shouldn't be contained directly in the source code

  • Environment-specific feature flags

  • Though Vue CLI keeps track of the build mode while it builds your application, that information is not stored in any environment variable. If you want to be able to check explicitly what mode your app has been built in, you should set an environment variable with the mode name.

Reading Environment Variables In Your App

One important thing to know about these variables is that only environment variables with the prefix VUE_APP_ will be readable off the process object from within your Vue application at run time.

In the interest of keeping your application clean, it's best to build a small service that's responsible for managing your application constants. This service can then also be responsible for reading the process.env.VUE_APP_ variables and re-exporting those values as constants without the redundant prefix to other anywhere they're needed in the application. That way, all your environment-dependent code can be centralized in a single place.

Configuring webpack

While environment variables will let you write conditional logic in your Vue app, sometimes you will want to conditionally change how your project is actually built. Under the hood, Vue CLI relies on webpack to build and run Vue projects. We can fine-tune those webpack builds by using the vue.config.js file. This is not a 1-1 copy of how you'd normally configure Webpack, as certain common webpack configurations are handled slightly differently. https://cli.vuejs.org/config/#global-cli-config

One common example of mode-specific context needed at build time is the application's public path. In our case, the URL where we hosted our staging server was different from the URL for the production application. Therefore, we must access an environment variable in vue.config.js to set the publicPath differently according to the current build mode.

Development vs. Production Builds

As mentioned before, the Vue application build mode isn't the same as setting the webpack build mode. The webpack build mode controls how the project is compiled. Webpack has a different set of instructions for how to build the project according to whether or not the process.env.NODE_ENV variable is equal to development or production. NODE_ENV is normally set by the system on which the project is built, but it is often desirable to control it explicitly, either to avoid bugs or just make sure that your testing/staging environment resembles production as closely as possible.

To control the webpack build, you can add custom configuration to the vue.config.js file, use the library cross-env in conjunction with your build scripts in package.json.

Putting it all together: Configuring A Staging Environment With a Unique Deployment Path

In order to solve our problem, we needed to do two things. The first was to set environment variables with the base path for our application.

VUE_APP_MODE = staging\
VUE_APP_BASE_ROUTE = /staging/
VUE_APP_MODE = production
VUE_APP_BASE_ROUTE = /

In this example, we're expecting our staging app to be deployed in a subdirectory called "staging" while the production application will be deployed at the root. We then read these variables in through our constants service:

export const BASE_ROUTE = process.env.VUE_APP_BASE_ROUTE || '/'

And in vue router:

const router = new VueRouter({
  base: BASE_ROUTE\
})

This way our router will work for our different base routes. Next, we need to update the vue.config.js file to have a different publicPath per environment. We can read in our own environment variables to do this:

module.exports = {\
  publicPath: process.env.VUE_APP_BASE_ROUTE,\
}

Because this is a js file we could even do conditional logic to configure parts of the webpack build. For example, if we want to separate the webpack build mode from the NODE_ENV variable, we can set it here.

module.exports = {\
  publicPath: process.env.VUE_APP_BASE_ROUTE,
  configureWebpack: {
      mode: process.env.VUE_APP_MODE !== 'development' ? 'production' : 'development'
  }\
}

You can then test your build mode by running

vue-cli-service serve --mode staging

And see your changes running (by default) at <http://localhost:8080/staging/>

Or build your project in staging mode by running

vue-cli-service build --mode staging

... and we're ready to deploy our new staging environment!

Wrapping Up

After going through the process of setting up a custom build mode, you'll discover as I did that Vue CLI's strength lies in its extensibility. It offers the best of both worlds by providing a quick way to bootstrap a Vue app without having to deal with configuration, but also a great deal of flexibility to adjust to the specific needs of not only your app, but its environments. Whether you're working with Vue for your next project or just trying it out, take some time to familiarize yourself with Vue CLI.