Progressively migrating from AngularJS to Vue.js at Unbabel

We recently decided to migrate some of our existing web applications from AngularJS to Vue.js and set Vue as the new standard framework to create UIs at Unbabel.

Because completely rewriting an existing production application would be very time consuming and prevent the application roadmap of moving forward, we’ve found it’s almost always better to make small changes and release often. The only requirement here is to have a clear vision about where you want to be when it’s done.

Doing this, we were able to progressively migrate our old Angular 1.2 applications into Vue.

We can break the process down to the following 3 steps:

1 — Baby steps

The first application we decided to migrate is part of what we call The Core, a monolith that aggregates several services within Unbabel — a perfect application candidate for what we wanted to do.

Configuration-wise, we want to keep things simple. Fortunately Vue comes to our rescue. In the Getting Started section you’ll find that you can get started just by including a script on your page and start writing a Vue component.

Including Vue in your page

Because it’s so easy to use, we were able to create the first components without any extra tools and continue to deliver new features. One of the first things we developed was a new sidebar for our application.

Creating your first component

Once this new set of components was done and integrated into the page, it was time to connect it to the main application.

Our new sidebar receives information from the main application and displays it in a specific format. Because we are already including jQuery on our page, one very simple way to communicate between Angular and Vue is to use jQuery Events to pass information around. We can use .trigger()on the Angular side and then catch the result using .on().

Sending data from Main application to Sidebar in Vue

When the code in main-application.js runs, the sidebar will catch the event, receive the data and call the internal method handleUpdateSidebarData which then updates the component state.

This way we can easily communicate between the two sides using existing tools and if we need to send data from the sidebar to the main application, we just do the opposite.

If you need to have Vue components inside our existing Angular application, that is also possible.

Create a mounting point for your Vue component, append it to the desired location on the target DOM element, then create your component and mount it to the previously created mounting point.

Don’t forget to call $destroy() on ComponentVM when not necessary anymore to clean up its connections with other existing vms, unbind all its directives, and turn off all event listeners.

Illustrated example of Vue components inside Angular controllers

2 — Preparing the tools to scale

As developers, we like tools that make our life easier and allow us to be faster and better at what we do. After moving the application templates and assets to its own project, it was now time to implement the best tools to develop with Vue.

Just like React has create-react-app, Vue also has a similar set of tools that provide developers with Webpack configured with hot reload, linting, css extraction and vue-loader, which enables you to use the awesome Single File Components. You can find it here vuejs-templates/webpack.

We wanted to have all these great tools because it makes developing with Vue a lot better, so we downloaded the template and adapted the configuration files to our own project structure.

Once the tools were properly set-up, we had the task of moving the components we had created before and move them to Single File Components, this was quite simple, we just needed to merge together the template and the component we had created before, into the same file.

Here you have a simple Vue component with its template declared in HTML.

When used, it will render <h1>Hello World!</h1>

And here is the equivalent component written using Single File Components

All the code related to this component is in one single file, so its much easier to maintain and to understand.

Take a look at vuejs-templates/webpack if you want to start using Single File Components right away.

Managing common dependencies

If you have dependencies that are shared between Angular and Vue, such as jQuery, you should add those as a requirement of your Vue application and expose them to the window. This will allow jQuery to be used by the Angular application just like before.

If you’re referencing jQuery through $ variable, you’ll also need to expose it to window like this window.$ = jQuery

3 — Healthy growth

Having tests is a crucial part of any web application that needs to scale and be maintained properly, so we definitely had to have those. We made sure to have our new components fully testable from day 1 and eventually, after some tests with different frameworks, we decided what unit test tool to use.

Currently we are using Jest by Facebook. It’s a very productive tool from the first minute you install it. Depending on your development configuration, the first and only thing you need is to create any file that matches with .test.js or .spec.js, write some tests and then run Jest. It will automatically find any files whose name matches the previous pattern and make any assertions inside. Take a look at the getting started section to have a proper look on how to start using Jest.

Jest also reports code coverage if you run it with --coverage as an option. You can use this option to understand what parts of your code you haven’t tested. You don’t need to strive for 100% code coverage, if you’re above 80% you’re already pretty good.

Example of code coverage reported by Jest using the included Istanbul Code Coverage

Because you’re reading this article, you’re probably already using Vue or planning to use Vue, so here are some tips on Jest that may be useful given the motivations of this article.

Add "test": "jest --coverage" to the scripts section in your package.json file, then run the command npm test to see the results. As a reminder, any other commands in the scripts section can be executed with the following command npm run :script-name:.

You can configure Jest by creating a jest.config.js and placing it in the same directory as your package.json file.

Using relative imports

If you’re importing your files using the @ alias, for example import Header from @/components/Header, you’ll want to have the same behaviour and ease of use in your Jest test files. To have that, you need to add the following configurations.

  • rootDir is where your source code is located relative to the config file.
  • moduleNameMapper is the equivalent of Webpack alias. @/Utils will load the file ./webapp/src/Utils

This will allow you to use the relative imports you already have in Webpack.

Adding Babel and support for .vue files

If you are using ES6 features you will need to include Babel (no relation to us!), and if you’re using .vue files you need to include support for those as well.

To do that, you need to run npm install --save-dev babel-jest jest-vue to add babel-jest and jest-vue, then you need to add the following transforms to jest.config.js

Defining mapCoverage to true is important for jest-vue to report proper code coverage.

Mocking dependencies

Another thing you’ll eventually run into is the need to mock project dependencies. A good example is a service that makes network requests to fetch information.

In this example I show a function that preforms a network request, and returns a Promise with the result in JSON.

If you’re testing a component that has Api.js as a dependency, you will want to mock this service and return a specific output when fetchData() is called.

To do that, you first create the mock version, like so.

As you can see, both files implement the fetchData() method, so we can easily swap one with the other and our code can be executed as expected.

Now we need an easy way to say to Jest that when @/Api is imported, we want to return @/ApiMock, and we want to do this without modifying our source code in any way.

Jest has a whole page talking about mocking function, but the essential to know how to handle the previous example is the following. You have to create a file where you will define your mocks, let’s call it setup-mocks.js.

Inside you will declare that, when a file requests @/Api, Jest will return @/ApiMock instead.

The final step is to add this file to your setupFiles option inside jest.config.js.

setupFiles: [
'<rootDir>/src/tests/setup-mocks.js',
],

Now, when you run your tests, the method fetchData() will always return a Promise with the value { message: 'This is a static value', } instead of doing a network request.

TL;DR

It’s very easy to migrate from Angular to Vue. You can start by including Vue in your page with a single <script> tag, and author your first components. When you’re ready to grow, you can add great tools like Webpack that offer hot reloading and allow you to use Single File components. You will need to add tests to your application to guarantee sustainable growth, and for that you can use Jest, it’s very simple to configure and start writing tests.