I Moved Away From Webpack — and When I Loaded Vue Directly in the Browser I Faced Some Problems.

Why do I need a bundler anyway? Come with me on a journey on how to load Vue single file components directly in the browser.

Lukas Gamper
Better Programming

--

Over months my frustration with Webpack built up, after every update something had to be fixed and it started to wear me out so I asked myself: Why do I need a bundler anyway? Can the app run directly in the browser? The browser APIs have grown up, just give them a try!

So my first goal is to create the most simple Vue application I can think of. Just import the createApp function from Vue and display a simple template on the screen. How do I load Vue in the browser? The Vue documentation knows the answer: The Vue source code can be loaded via a content delivery network:

When I open the app in the browser, I realize: I’m on the right track! I can run Vue in the browser without a bundler.

But writing the whole app in the main.ts file single is quite inconvenient. When I was using a bundler, I could split the app in components, using the Vue single file components.

If the bundler can do this, why should the browser not be able to do this? Come with me on a journey to load single file components directly in the browser.

Loading a Single File Component directly in the Browser

The application above is pretty simple, it’s only a template without logic. If I do the same in a single page component it looks like

Hmmm, how do I load a file like this directly in the browser?

  1. Using the fetch API I can load the component file from the server.
  2. Then the file can be transformed using the innerHTML property into a DOM representation of the component.
  3. The querySelector allows to extract the template element.

This is all the magic! With this function I can import the very simple ChildComponent.vue

If I open the browser, I know, what should be displayed:

Yes! I managed to load a template from a Vue single file component! That’s awesome! But a Vue component without logic has quite limited use. What about the logic?

Add Logic to the Components

The ChildComponent is becoming a full real wold Vue component with template and logic.

But its’s not as easy as it seems in the first place we can’t just evaluate the script using innerHTML. Why? The HTML5 specifications on innerHTML tells me why:

Note: script elements inserted using innerHTML do not execute when they are inserted.

This means the browser is not going to help. So I have to do it on my own.

My first thought was to create a <script> tag or use the ugly eval function, but the <script> tag or the eval function are dinosaurs from the pre module age. But a Vue single component file has a module with a default export in its logic part. This means the two options above are not valid options.

There is a third, less known way to evaluate a module: If I represent the logic as a data URI, the import() operator lets me dynamically load the JavaScript modules:

The code above will show the alert window

This is the trick I was looking for. Using this trick, the single component file import function becomes.

Now my single component file loader already supports templates and logic. Now, I can use Vue in the browser without a bundler. But after some time, the style sheet gets quite messy. What about using styles in the single-component file?

Styling the Components

This is easier as expected. The only thing I have to do, is to move the style element from the single file component to the actual DOM. This can be done with a single line of code:

That’s it! We can use single component files with templates, logic and styles.

But as soon as I introduce my first submodule nothing works anymore. All our imports in the single-component files broke.

Fixing Dynamic Imports

First of all: why are they broken? Well to be fair, how should the browser be able to resolve relative paths? I evaluate the logic using the import trick and by doing this, the browser looses the context. The browser has no chance to know the relative path of the module.

This is the final problem. If I manage to fix this, I can run a complete Vue application in the browser using single component files.

To fix the imports I need to replace all the relative import paths with absolut paths. This code is a bit more tricky, but essentially, I need to do the following three steps:

  1. I need to determine the base url of the imported single component file. This can be done using the <base> to transform the relative path of the module into an absolute one.
  2. Find all import statements using a regular expression
  3. and replace each relative import path with an absolute one.

The final single component importer becomes a bit more complex, but with just 50 lines of code, I can import Vue single component files! This is unbelievable! We can just run Vue single-file components, without a bundler. And now comes the BUT: This is a nice technology demonstration, but do not use this in production.

So I went back to Webpack and after wasting many hours googling, I solved my Webpack conflicts and use Webpack ever since.

Conclusion

This example shows how the web has majored over the last decade. Native Browser APIs have already reached the point, we can run a full Vue application without a bundler. The Browser API’s become more power full each year and if the development continues at this pace, many tools used today may become obsolete in just a few years.

This is incredible! It is possible to import a Vue single file component with just 50 lines of code. But this can not be compared to the @vue/compiler-sfc. The solution above, lacks many features like scoped styles, other languages like TypeScript in the script tag, or hot module replacement. The is still a long way to go until the browser can provide a similar developer experience to a bundler.

--

--