Migrating from jQuery+UI to VueJs

Thomas Amsler
Valtech Switzerland
10 min readMay 15, 2020

Through the eyes of a back-end developer with limited front-end know-how

Coral reef near Komodo, Indonesia

In Summer 2018, we had the idea to completely refactor the front-end part of our new PDF as a Service, which was built on jQuery+UI, to Vue.js. The two main challenges were:

  1. Limited resources: With a small budget for development, we only had two developers working 1 day per week on this.
  2. No one had done a Vue project before

But first a brief history of this service:

It started as a customer idea who was using the enterprise CMS Adobe Experience Manager (AEM), so the authoring could have be done using custom AEM components. But this would limit us to AEM which comes with a rather expensive license. Nonetheless I started with some prototyping of a browser based layout editor, to see how this part could be done. I mainly knew jQuery+UI so these were the libraries I chose to build the front-end part.

In March 2018 I was just coming back from a sabbatical and didn’t have a project yet (the picture above was taken during that time). So I decided after many years as a pure AEM developer, to learn about micro-services. A former colleague had a successful project made with Spring Boot, so I read tutorials and watched some videos. Then the idea grew to use the prototype I created before and having a Spring Boot back-end instead of AEM. I made quick progress and a version connecting the front-end to the back-end was ready by end of April. Soon after I started on a new customer project, but got the budget to continue on this prototype during one day of the week. I also got some front-end support from colleagues between projects; I can code Java and JavaScript and know basic CSS, but I am no designer. Some of them were talking about VueJS as the next big thing in front-end development. As we anyway have decided at Valtech (back then still Infocentric) to phase out jQuery for new projects, I created an epic simply called “Use Vue” in our backlog. There we collected ideas and migration paths and later some actual stories were created.

I was mostly absorbed with AEM projects for the last 10+ years; I started when it was still Day Communiqué, short CQ and version 5.1. So I only knew jQuery as a front-end library and had no idea about modern front-end stuff. Luckily one of the front-end developers which supported me already from time to time was eager and got the ok to also spend one day per week on this service. He also introduced a first third party Vue component: Vue-good-table for the main list views which were rendered in the back-end until then and had no sorting and filtering. So we could actually start this migration task with his help; or rather he could start, as I was still polishing stuff in the back-end.

The journey begins, very slowly

As it is with modern front-end frameworks, you can’t just add a script tag to your html which contains the minimized version of the vendor and start using its API. You need a build tool and also have dependencies to third party scripts. For me, this was something I only knew from Java. Of course I heard about Node.js and webpack but had no idea what they imply not to mention how to use them. Though the front-end developer already introduced Node for the linting of our JavaScript and CSS files and to package them, I was just using one command, “gulp”, whenever I had some changes in the CSS — and only because I found it in the README.

Vue CLI, the build tool for VueJS was added to the build and our first own Vue component was created: A modal to replace the jQueryUI dialog widget. We didn’t trust our own capabilities yet, so we had both versions in the code base and had to comment and un-comment lines of code to switch between them. So it was still very experimental. We also hadn’t decided how we are going to structure our components. The Vue-Good-Table, as an example, is implemented very simply by having a panel below the top navigation to act as our Vue root element and then we just converted the global, but namespaced, jQuery functions to be methods within that root component, but still using jQuery for AJAX and DOM manipulations outside of this table. So this part didn’t even need VueCLI yet as it was just simple JavaScript wrapped in jQuery:

(function ($) {
'use strict';
new Vue ({
el: '#panel',
data: function () {
return {
items: [],
};
},
mounted: function () {
this.load();
},
methods: {
load: function () {
var that = this;
$.ajax({
type: 'GET',
url: pdf.contextPath + 'list',
success: function (data) {
that.items = data;
}
});
},
},
});
})(jQuery);

Hiatus and restart

While writing this, I was first surprised how old that initial Epic was, but when I checked our Git history, I understood my confusion. We didn’t merge any of our own Vue components to develop until fall 2019. The reasons are simple, we sold our first subscriptions to clients by the end of 2018. So we had to polish the jQuery version to be really production ready and the clients also had some feature requests, we implemented for them. This was occupying our sparse time on this service until summer 2019. But by then I also started to dig into Vue development and we finally decided on the code structure:

  • Vue components are in a separate folder outside of the Spring Boot build and the build just copies the generated distribution files to the resources folder of it. We initially also committed these generated files to Git, but after endless merge conflicts we no longer do this.
  • Each component has a folder in which reside four files:
    index.vue → as you would do for single file components, but it only includes the other three
    script.ts → we decided to use TypeScript with the help of the vue-property-decorator
    template.html → self speaking
    style.css → if there are custom styles just for this component
  • Use interfaces for our REST API responses
  • Using axios for the ajax calls

With this settled I created my first few components which were independent views, so the impact was minimal. We continued to have a mix between Vue and jQuery as we didn’t feel comfortable to get rid of it yet.

Picking up momentum

With the success of these first few components we both started to feel more comfortable and we started to tackle view after view and soon our generated files were getting too big as the build started to show warnings. So we had to rethink our build process and we decided to split the components:

  1. common → shared components like our very first “modal”, some defaults for axios, the navigation which was also migrated from rendered by the back-end to a Vue component and the Font Awesome icons we use
  2. vue-good-table → wrapper for this third party library and some CSS overrides from our side
  3. for each view there is a separate library and as they have very specific functionalities, there are only 2 Vue components that are used in more than one library, else they would be moved to the common library

With this we managed to convert all of our views to Vue components, except one: The layout editor, but more about this later. We also converted the simple JavaScript versions shown above into own components, using TypeScript and getting rid of any jQuery within them. We also got rid of the jQueryUI styles we introduced and even used within our modal component for looks .

Last but not least

During this whole migration process we feared the layout editor the most. This is after all the most complex part of the whole front-end. Using Drag&Drop and Resize from jQueryUI together with a lot of custom code for the different elements. So this was a beast to tackle and we first needed some research and prototyping. For the two jQuery UI widgets, we needed to find third party Vue components or we would have to implement the rather complex logic by our self. Luckily I found two such components which suited our needs:

It may seem weird to use two different components which implement Drag&Drop, but their use cases are very different. Whereas the first also implements the resizing part which is exactly what we needed for the layout editing, the second is meant to sort and nest lists which is also needed in one of the panels of layout editor.

This also forced us to not copy anything from the jQuery version, but to rebuild the functionality. This had many advantages:

  • less code → the jQuery UI widgets needed a lot of custom code to work, the Vue components just needed a fraction of it to achieve the same
  • structured code → these components and how Vue works in general: https://vuejs.org/v2/guide/components.html (props down, events up) made the code clearer and less scattered
  • better usability → some parts were a bit hacky in the jQuery version and as we had to rethink a few patterns, the result is a clearer UI

Cleanup

After we managed to merge this “last” huge branch into develop it was time for some housekeeping. As we were learning Vue along the way, the first components weren’t to our liking anymore, so we did some refactoring and optimization. On top of that we introduced more strict linting rules and enforced some of them. We weren’t able to lint everything we wanted, so we had to rely on some manual labor and code review on our side. Here is what we decided for our TypeScript part of the Vue components:

Order of imports:

  1. vue-class-component → official TypeScript support
  2. vue-property-decorator → 3rd party TypeScript annotations, inherits vom vue-class-component
  3. third party → any third party library or component
  4. functions → own utility functions
  5. interfaces → own interface defintions
  6. mixins → own mixin components
  7. classes → own actual TypeScript classes
  8. components → own vue components

Order and naming within the component class:

We decided to have most of the members within the class public and explicitly use the “public” keyword, even it would not be necessary.

// static members and functions
static
// public members (in a Vue component written in JavaScript this would be the attributes of data()
public x
// private members like internal states that are not used in the template
private _y
// props are readonly at runtime so we declare them readonly in the TypeScript as well
@Prop()
public readonly p
// refs are DOM nodes so you can define their type directly and they are thus also readonly,
// you could achieve the same with "this.$refs.r" within some code block, which can be easily missed
@Ref()
public readonly r: HTMLElement;
// we use watchers mostly on props if they could be changed from the parent and we need to react on it in a special way
@Watch('x')
public watchX()
// We use the Emit annotation and the method name prefix to make it clearer that this method informs its parent,
// you could achieve the same with "this.$emit('y')" within some code block, which can be easily missed
@Emit('y')
public emitY()
// a special Emit that gets fired if the user interacts with the template, e.g. a click, thus the additional "On" prefix
@Emit('z')
public emitOnZ()
// getters in Vue class components are the computed variables
public get x()
// event handlers have the prefix "on"
public onZ()
// we don't have many of these plain public methods left, except the default "created()" and "mounted()" hooks from Vue
public y()
// private methods that are only used within this class and not in the template even though the template would have access to them
private y()

Conclusion

Don’t be overwhelmed when doing a migration. If you do it smart, you can start with small parts and even have the two libraries work hand in hand. There is only one pitfall in this approach, don’t ever modify the same DOM element with Vue and jQuery, decide which library is responsible for which part or rather let Vue do the DOM manipulation and let jQuery only access it read-only.

If you are used to some convenient jQuery methods to access the DOM, nowadays JavaScript in most browsers can also query the DOM by CSS selectors. But with Vue you don’t even have to. For many jQueryUI widgets you’ll probably find a Vue component that does something similar or even better. And as mentioned before axios is a good replacement for jQuery.ajax.

Next Steps

The idea is to release version 3 of our service by the end of 2020, which has the following changes to the current version 2:

  • Use the latest Sprig Boot version 2.2.X (we currently use 2.0.X )
  • Use JUnit 5 instead of 4 for the back-end tests
  • Use Java 11 instead of 8 (the points above need it anyway)
  • Use Vue 3 instead of 2, but the limiting factor is if it gets released soon.
  • Dockerize the whole project for easier deployment

So for the front-end part the next challenge is the new version of Vue where we hopefully don’t have to change much. What is known so far suggests that there will be only slight changes and many of them optional.

Glossary and Links

I linked all the mentioned libraries and components in the text, here an alphabetic list and a short description for your convenience:

--

--