How to Integrate External DOM-dependent Libraries Into a Vue Application

How to Integrate External DOM-dependent Libraries Into a Vue Application

Integrating 3rd party libraries into a Vue application is not harder than in any other JavaScript reactive framework. Nothing special with using a single import statement. Integrating a component that depends on DOM and renders itself on a page is a different story. Examples are maps, editors, image manipulation tools and so on.

There are three common approaches to integrate an external library into a Vue application: directly use it on some level of the application's component hierarchy, write a plugin or create a wrapper component. If there are multiple places where the library will be used and it renders some HTML, wrapping it into a component is a good choice.

Wrapper component is a regular component that when mounted initializes a library with a DOM element from inside its own template.

mounted: function () {
  $(this.$el).myFancyLib({ data: this.options });
	  ...
},

Next implementation step for a wrapper component is to map the library's events and properties to component's one. Here is an abstract example of an input component:

mounted: function () {
  var vm = this
  $(this.$el).myFancyLib({ data: this.options });
    .on('change', function () {
      vm.$emit('input', this.value)
    })
	  ...
},     
watch: {
  value: function (value) {
    // update value
    $(this.$el)
      .val(value)
      .trigger('change')
  },
  ...

You can use the same approach to integrate a library inside your Vue application without creating a wrapping component. Steps are the same:

  • Initialize a library inside mounted() lifecycle hook with an element from the component's template. For example, this.$el which is the component's top HTML element
  • Establish library <-> component communication using watchers, events and properties

Whatever approach you use, if a library renders something and so that depends on an HTML element it was initialized with, you should take care of changes in the element properties. Although Vue.js encourages developers to use "data-driven" approach, a dom-dependent 3rd party components are a good example of a situation when we have to get our hands dirty.

Vue.js uses async update queue to render changes in component's template. When you update a property, a component HTML output is not immediately changed, but on a next "tick". That means that if you want to respond to changes inside a watch method and that method includes calls to a library API, you should be aware that you will not have access to an updated DOM at the call time.

A practical example of this limitation would be any component that renders itself in a containing element (div or canvas). Fortunately, there is a solution. For example, if you have a property that controls the size of the element and have to rerender object when containing div dimensions change, then you can use the nextTick method:

watch: {
  height(newVal: number, oldVal: number) {
      this.$nextTick(function() {
        if (this.$refs.something) {
          this.$refs.something.onResize();
        }
      });
    }
  ...
}

Summary: if you need to directly access or manipulate DOM, or library you use is depended on the DOM, access rendered elements inside the nextTick callback.

Another typical pitfall when integrating 3rd party libraries into a Vue component is to forget disposing resources at the end of component's lifetime. That leads to memory leaks and slows down an application. Just remember to put library's resource-freeing code inside the beforeDestroy() lifecycle hook. How to debug and fix memory leaks in a Vue application is a topic for another article.