I am doing something similar right now, the project was originally written using Thymeleaf and jQuery and now we are switching to Vue.
You can communicate between vue components and non-vue components in a few ways, none of them are "pretty".
Communication
Classic JavaScript events
Pretty straightforward
// legacy code
document.dispatchEvent(new CustomEvent('legacy-vue.something-happened', { detail: payload }))
// vue component
created () {
document.addEventListener('legacy-vue.something-happened', this.onSomethingHappened)
},
destroyed () { // don't forget to remove the listener!
document.removeEventListener('legacy-vue.something-happened', this.onSomethingHappened)
}
Exposing EventHub
Similar to the previous one, but you are using vue events instead. This is what i would recommend, because it's the Vue way of handling events and your goal is to vuetify your app.
// initialization
const hub = new Vue()
Vue.prototype.$eventHub = hub
window.$vueEventHub = hub
// legacy code
$vueEventHub.$emit('something-happened', payload)
// vue component
created () {
this.$eventHub.$on('something-happened', this.onSomethingHappened)
},
destroyed () {
this.$eventHub.$off('something-happened', this.onSomethingHappened)
}
Exposing whole components
The most flexible way, but it's hard to see what's going on where. Event based approaches the lesser evil in my opinion (it's easy to track events).
// vue component
created () {
window.vueTableComponent = this
}
// legacy component
vueTableComponent.fetchNextPage()
vueTableComponent.registerOnPageFetchedCallback(callback);
Summary
No matter which approach you pick, i recommend doing something like this:
Let's say that you have TableComponent. TableComponent has few props like apiUrl, emits row-clicked event, etc. It would be best to design the component without thinking about your legacy code at all, and then create it's legacy wrapper because at one point it will be used both with vue-only screens and with mixed-screens (with both legacy components and vue components). An example:
LegacyTableComponentWrapper.vue
<template>
<table-component
:api-path="apiPath"
@row-clicked="onRowClicked"
ref="table-component"
/>
</template>
export default {
data: () => ({
apiPath: null
}),
methods: {
onRowClicked (row) {
this.$eventHub.$emit('table-row-clicked', row) // notify legacy code
},
onApiPathDefined (payload) {
this.apiPath = payload
}
},
mounted () {
// legacy code might require the TableComponent to act differently
// and if you don't want the TableComponent to know whenever it's legacy or not
// you can always override one or more of it's methods.
this.$refs['table-component'] = this.overriddenImplementationOfXYZ
},
created () {
this.$eventHub.$on('define-api-path', this.onApiPathDefined)
},
destroyed () {
this.$eventHub.$off('define-api-path', this.onApiPathDefined)
}
}
It's sure more work at first, but will save you a headache later, when you will be working on your first view which is fully in vue and all that legacy communication stuff is in the way.