
Migrating a Vue App to Vue-CLI
As with most software projects, when working on a Vue app for a couple of years, there comes a time when the framework developers have made breaking changes, but have also added interesting features that make updating the framework both desirable and painful. In our case, we were maintaining and improving a Vue app that was a couple of years old already and still using Webpack 3 as a build tool. Large and seasoned apps usually look back on a broad history of changes to the build pipeline and dependencies that make simply updating Webpack unfeasible. However, as Webpack 3 grew older and the community moved on, it became obvious that it was high time for us to get ourselves up to date. Looking at the available options, we found that Vue CLI offers a very clean new way to manage build pipelines and dependencies with less code involved. So I decided to take the plunge and migrate our app to Vue CLI cleaning up a lot of legacy code along the way.
General approach
Before writing any code, it’s best to first take stock of your dependencies and make note of which ones you still need and which ones can be dropped. For a newer build pipeline you might be able to replace some dependencies with new or different implementations. You should also have an idea about all the things your build pipeline is actually doing so that you can test for that when you’re done migrating. When compared to the Webpack 3 build, I found that the Vue CLI build process works very different in a lot of places. So when thinking about how to approach the whole migration I came to the conclusion that, instead of performing an update on the current project, it would be best to create a new Vue CLI project altogether and then perform a piece by piece migration of the various parts of the build process along with only some of the components. With each new piece added to the new project, I would then make sure that everything still runs they way it’s supposed to.
Starting a Vue CLI project
Setting up a Vue CLI project is explaned very well on their offical page, so I’ll just give you a cursory quickstart here. If you haven’t done so already, you need to first install vue-cli:
npm install -g @vue/cli
Then just create the project with a name of your choosing:
vue create my-project
This will start the project creation wizard asking you about which features to include such as typescript support, unit testing and more. And that’s it! You should be able to run your project now.
cd my-project
npm run serve
Webpack aliases
We’re using Webpack aliases to provide shorthands for frequently used directories. These can be configured in Webpack like this:
// webpack.conf.js
module.exports = {
...
resolve: {
alias: {
"@": resolve("src"),
...
}
}
...
}
For build configuration Vue CLI uses a special file now called vue.config.js
. It’s an
additional layer on top of the Webpack configuration itself. You can read more
about it in the official docs.
The exported module is used by Vue CLI to produce a webpack config under
/node_modules/@vue/cli-service/webpack.config.js
.
You can still make changes to the Webpack configuration using vue.config.js
in a similar way via the key configureWebpack
.
// vue.config.js
module.exports {
...
configureWebpack: {
resolve: {
alias: {
"@": path.join(__dirname, "src"),
...
}
}
}
...
}
And that’s all that’s needed to customize Webpack again.
A quick note on static assets and path shorthands: If you are referencing
assets like background images using aliases via inline html attributes or css, you need to use
the ~
keyword for referencing the path. So if you want to reference your logo
in src/assets/logo.png
and your alias is configured as shown above:
// resolving in inline html attribute
<div src="~@/assets/logo.png"></div>
// resolving in css
<div class="logo"></div>
<style>
.logo {
background-image: url("~@/assets/logo.png");
}
</style>
Vue CLI Plugins
Vue CLI provides a plugin API that should be used whenever possible instead of npm
when adding
new dependencies. Many major modules already provide CLI plugins like the
fantastic css framework Vuetify. You can install such
plugins by simply calling vue add
with the plugin name.
vue add vuetify
If the plugin can be found, it will be installed and can make changes to the
vue.config.js
. Some plugins may also guide you through their own setup wizard
after installation. Afterwards, you can initialize and import your plugin in the
main.ts
as usual.
Webpack-Chain
Vue CLI makes use of webpack-chain internally
and exposes it in vue.config.js
. It’s another abstraction layer that’s
convenient for modifying plugin and loader options.
For different build flavours I needed a way to copy some static files from the flavour’s
directory to dist
. Vue CLI comes with a copy-plugin preinstalled. In the vue.config.js
,
I just needed to add a function called chainWebpack
to the module with a single config
parameter.
To modify the copy-plugin, I could then access it by name and “tap” into its arguments.:
// vue.config.js
module.exports = {
...
chainWebpack: config => {
// copy flavour files to dist folder
config.plugin("copy").tap(args => {
args[0].push({
from: "/flavour/path",
to: "/dist",
toType: "dir",
});
return args;
});
}
}
Loaders can be modified in much the same way. Let’s say you want to add a new
loader for plain html files. First install the html-loader
module.
npm i -D html-loader
Then simply add a new rule for html-files and specify the loader to use.
// vue.config.js
module.exports = {
...
chainWebpack: config => {
// loader for some plain html-files
config.module
.rule("my-html-rule")
.test(/\.html$/)
.include.add("path/to/html-files")
.end()
.use("html-loader")
.loader("html-loader");
}
}
Migrate Vue components piece by piece
Now you should be ready to migrate some Vue components. You may pick a topical chunk of components that run together, install their dependencies and see if they work as they should. It’s probably best to first get your Vue-Router ready if you’re using it. This step should be straightforward. If you don’t have that many components you might as well move them all at once, but doing it in a piece by piece fashion also helps you spot some legacy code and dependencies in your components that you don’t need anymore.
In this step, you might encounter a build warning that looks something like this:
warning
chunk offer_create~offer_detail [mini-css-extract-plugin]
Conflicting order between:
* css ./node_modules/css-loader??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??postcss!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/views/Offer/components/application-questions/application-questions.vue?vue&type=style&index=0&id=69b13b08&scoped=true&lang=css&
* css ./node_modules/css-loader??ref--6-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??postcss!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/MutipleEvents.vue?vue&type=style&index=0&id=fe6da6ea&scoped=true&lang=css&
...
This happens if you have multiple components importing the same styles, but each
does so in a different order. You need to determine if this actually causes problems
for you, but chances are that it won’t if the App was working fine before migration.
You can read more about this warning here.
If you have safely determined that this does not cause problems for you, you may
want to just ignore the warning in your vue.config.js
like so:
// vue.config.js
module.exports = {
...
css:
// css plugin should only be active in production environment
process.env.NODE_ENV === "production"
? {
extract: { ignoreOrder: true },
}
: undefined,
}
Environment Variables
Runtime Variables
If you’re using environment variables during your app’s runtime, you need to be
aware that Vue CLI removes all environment variables after the build. This is
done to prevent accidentally exposing secrets through those variables. For any
variables that are relevant beyond the build time of your app, you can prevent
this behaviour by prefixing it with VUE_APP_
. So if you had a variable called
API_URL
before, you have to rename it to VUE_APP_API_URL
.
Variable files
Vue CLI uses .env
to read environment variables. Depending on your use case,
you may want to move your variables there. You can also create .env.local
files which
are preconfigured to be ignored by Vue CLI’s generated .gitignore
. To specify variables
for a specific runtime environment, you can put the environment name as a file suffix.
From the docs:
.env # loaded in all cases
.env.local # loaded in all cases, ignored by git
.env.[mode] # only loaded in specified mode
.env.[mode].local # only loaded in specified mode, ignored by git
You’re done!
That about covers all the most important aspects I had to deal with for migrating the Vue application. Of course, don’t forget to adjust your Readme file for you fellow developers so that they may easily understand how to properly handle all of those new aspects.
Good luck Migrating!
Picture credits:
Adobe Stock