Illustration for Migrating Vue2 without sourcecode

Migrating Vue2 App without sourcecode

November 2022

Or: How I ended up recreating a small subset of VueJS 2

Context

Last day I was looking for my old web-blog prototypes an found old web-app that I developed back in 2018 in VueJS 2 to test the framework. The main problem with it was that most of its content was lost (including the sources) and only bundled version were pushed into my backup system…

A snapshot of my mid 2018-2020 blog

A snapshot of my blog website, “Misete!”

Analysis of the bundled code

Hopefully It’s not lost, I have the source in the map right ? Let’s open the dev tools and read the JavaScript, it should feel familiar and …

Compiled VueJS code

The compiled VueJS code, barely readable

Well not really, at least there’s some variable name kept at compile time and the URL / Slugs are conserved too. Good to know, let’s see what we can do with it.

After opening VS Code and setting pretty-print on, I got the following pattern that showed up, driving me to think that VueJS 2 have some function that render node depending of their type.

VueJS compiled template code]

With some formatting, we start to see some interesting functions

I then started a new about:blank window in my browser and started to thinker around the script. Enjoyer of REPL-like experience, I did naïve script that define _c and _vm in the window, copied the script and executed, then looked at its output and fixed it until I got some basic system working.

Building a Virtual DOM

Let’s keep things simple, first eval the render function and build a Virtual DOM structure from it, then flush everything to console. Taking a look at how the functions are commonly used, I guessed a lot about VueJS 2 internals but couldn’t reproduce most of it for the sake of simplicity.

So from my understanding :

  • _c means “create element”, and look similar to the $createElement. Let’s set an alias and declare it.
  • _l is a loop function, like Svelte’s {#each } block. Let’s keep that in mind and put it aside for now
  • $t is commonly used in vue-i18n to translate strings. It takes a string as key

Each of the _vm Virtual DOM functions work with a Virtual Node (VNode) instance, that may be presented the following :

var VNode = function (tag, data, child, text, elm, context) {
  // this.val setters
};

I then created some simple builder for every function like so :

var _self = {
  _c: (tag, data, child) => {
    // create text VNode
    return new VNode(tag, data, child);
    // console.log(`_v ${val}`);
  },
};

You may notice that I defined them right in the window component, no wrapper or anything. It may be considered bad practice for production code but it’s super easy to thinker faster and patch on the fly that way.

Let’s test it with a basic render function :

function render() {
  var _vm = this,
    _c = _vm._self._c;
  return _c("div", [
    _c("img", {
      attrs: {
        alt: "Vue logo",
        src: "https://maleplate.dev/svelte-welcome.png",
        width: "25%",
      },
    }),
    _c("h1", [_vm._v(_vm._s(_vm.hello))]),
    _c("p", [_vm._v("Some paragraph test")]),
  ]);
}

And there we go ! A nice Virtual Dom tree structure :

Virtual Node structure generated from the render, printed in dev tools console

Getting Real from Virtual

Since we have a super simple VueJS 2 template parser, let’s de-virtualize it and create a real HTML DOM from it !

I wrote a simple helper to print the Virtual Dom tree :

function print(c, tab = "") {
  if (!c.tag) return;
  console.log(`${tab}<${c.tag}>`);
  if (c.children) {
    for (let child of c.children) {
      print(child, tab + "  ");
    }
  }
  console.log(`${tab}</${c.tag}>`);
}

That quickly got this nice HTML-like tree structure printed from it :

<div>
   <img>
   </img>
   <h1>
   </h1>
   <p>
   </p>
 </div>

The idea here is basically to chain document.createElement(vnode.tag) and recreate a VueJS-looking HTML from it. Since it’s a long task I won’t go into details here but the basic idea is to check the Virtual Node type and data, then loop over its child and build them recursively, to finally appendChild to the base element and attach it to the dom.

It worked great, except for the _vm.hello variable that wasn’t defined. At first I thought defining it (since that’s basically what VueJS does in the initialize part or your component) but it became hard once you got 10 more of them. Thankfully, the JS Proxy element saved me, mixed with the Function.call property saved me the manual task and produced decent mapping.

Finally, the long task was to get the data part of the Virtual Node to render correctly on HTML, but setAttribute with VueJS-like name did a great job.

Here’s the original test code from VueJs 2 I used to benchmark my script :

<template>
  <div>
    <img
      alt="Vue logo"
      src="https://maleplate.dev/svelte-welcome.png"
      width="25%"
    />
    <h1>{{ hello }}</h1>
    <p>Some paragraph test</p>
  </div>
</template>

And the generated HTML code :

HTML DOM generated

The generator did great

Visually comparing the two pages

And the webpage looked visually the same (including the global CSS)

Looking good ! Let’s now try in on real code

My VueJS blog, in HTML

What will the code do against much complex code ? Well, at first it crashed a few time because of missing _vm helper but thanks to the quick REPL experience of the blank page, it was easy to fix on the fly. Seeing the page getting back on the fly was an awesome feel, as the memories from the days I discovered VueJS got back and brought me back to 2018 for a while.

Then I got my Virtual Dom structure :

Basic Virtual Node tree from the compiled code, showing properties

That’s a great tree structure for such a small code !

And the generated code, looked a lot like what I have imagined :

Script output showing HTML close to VueJS syntax]

Generated HTML code for the homepage posts list

I finally got to a fully working Virtual DOM structure after 3 hours of fixes and researches. Some may say recreating it in Svelte or Astro would have took me 10 minutes, but now I have a powerful tool to parse any (hopefully ?) VueJS 2 built website and generate a near-VueJS template looking HTML.

It’s a great refresh for my blog that was dead for so long, and it’ll get back to my website soon !

Want more ?

I've got a lot of other blog articles

See them all !