DEV Community

pallade
pallade

Posted on

From Vue to Alpine.js on a legacy project

I have been struggling with progressive enhancement on a legacy project. The requirements are simple: there is some PHP-generated HTML that could be nicely enhanced.

Our approach has been using Vue 2 inline templates. This feature neatly separated HTML code (server generated) from JS and CSS code, that lived on the client.

No JS? No problem: the HTML works anyway.

Unfortunately, Vue 3 removed support for inline templates. This left us in a tough spot, since Vue 2 is unsupported and EOL.

Enter Alpine.js. You know Vue? Alpine is the same sauce. Minus lots of complcations. It is just perfect for this use case!

Migrating from Vue 2 to Alpine.js is really, really straightforward, and you gain a lot of benefits:

  • reactivity is super easy
  • reactivity and events bubble outwards, to the outermost Alpine.js enabled element. No need to have props and weird questions about communicating between parents and children!
  • data sharing among different components is easy: you can have a global reactive store and bind to that
  • all is inline! but you can have some separation of concens and move JS code to a separate file, so that the HTML becomes lighter
  • error reporting is stellar: look at your console and you will see a specific and well described error!

From file.vue to Alpine.js

Template (HTML)

If you have a component, it will have its own template, somewhere.

If it is an inline-template, then replace inline-template with x-data="customcomponent".

<div is="custom-component" inline-template>...</div>
Enter fullscreen mode Exit fullscreen mode

becomes

<div x-data="customcomponent">...</div>
Enter fullscreen mode Exit fullscreen mode

Otherwise, you probably have some <custom-component /> code in your HTML: copy and paste your HTML template that you have in your .vue file, and replace <custom-component /> with the entire HTML template. Then add and x-data="customcomponent" to the outermost tag.

What is customcomponent? It is the name of the JS component that we will create in the next step. The customcomponent will be registered at the last step of this tutorial, and will allow Alpine.js to connect this block of HTML to the correct script that automates it.

Note that you can (and should) inline x-data too, and have all your logic in one place. But I prefer to separate it as soon as it becomes even slightly complex.

Now that your HTML is mostly in place, you will need to replace the v- attributes with... x- attributes. You can just rename most of them: they are equivalent.

One exception is x-if and x-show: x-if works on a <template> tag only, so you should probably start by renaming v-if to x-show, unless you have special requirements.

CSS

Your CSS code goes to the main CSS. No magic here, sorry.

JS

  1. Create a new custom-component.js file
  2. Start like this:
export default () => ({
  // Code here
});
Enter fullscreen mode Exit fullscreen mode
  1. Paste the insides of your .vue script tag to where // Code here is.
  2. Promote all data to the outermost scope:
export default () => ({
  reactiveVar1 : 'hi!',
});
Enter fullscreen mode Exit fullscreen mode
  1. Promote all computed to the outermost scope:
export default () => ({
  reactiveVar1 : 'hi!',
  computeMe() : { return this.reactiveVar1; },
});
Enter fullscreen mode Exit fullscreen mode
  • Rename mount() to init()
export default () => ({
  reactiveVar1 : 'hi!',
  computeMe() : { return this.reactiveVar1; },
  init() : { reactiveVar1 = 'hello world'; }
});
Enter fullscreen mode Exit fullscreen mode
  • Have a watch block? Promote all the watching function to the main scope, and then connect them manually in your init() function, like this:

this.$watch('watchedVariable', (newValue) => this.watchedVariableChanged(newValue))

  • Adjust special Vue features, if you have any

Bring it all together now

You will need to bootstrap Alpine.js somewhere in your JS code, at the top level of your app, the same as you did with Vue.

You would do something like this:

import Alpine from 'alpinejs/dist/module.cjs';
import collapse from '@alpinejs/collapse'

import customcomponent from './custom-components.js';

window.Alpine = Alpine

document.addEventListener("DOMContentLoaded", function () {
    Alpine.plugin(collapse)

    Alpine.data('customcomponent', customcomponent)

    Alpine.start()
});
Enter fullscreen mode Exit fullscreen mode

We have imported our JS, we introduced it to Alpine and we gave it a name: now we can use it.

Go to your HTML file and replace data='{ someReactiveVar = 1; }' with data='customcomponent'.

Reload and enjoy!

Top comments (1)

Collapse
 
ekwoka profile image
Eric Kwoka

Protip: When using modules already, you don't need to do a document listener

import Alpine from 'alpinejs'
import Plugin from 'wherever'

Alpine.plugin(Plugin)

Alpine.start()
Enter fullscreen mode Exit fullscreen mode

Slap type=module on that script and you're golden.