Optimizing Vue.js apps with web workers
Tags: vue, webdev
Published: 2023-09-28 15:45:50

**Written by [Temitope Oyedele](https://blog.logrocket.com/author/temitopeoyedele/)✏️**

Single-page applications built with Vue.js often face performance challenges due to factors such as downloading and parsing large JavaScript bundles, reactive data binding, or the virtual DOM. Web workers offer an excellent solution for enhancing Vue app performance. 

Web workers operate independently within their threads, distinct from the browser's main thread. This separation lets web workers maintain functionality even when the main thread is occupied, ensuring your Vue app remains responsive during resource-intensive tasks. 

In this article, we will explore how web workers can significantly improve the performance of Vue applications by offloading resource-intensive tasks to separate threads, thus enhancing responsiveness and overall user satisfaction. Jump ahead:

*   [Understanding web workers](#understanding-web-workers)
*   [How web workers can help optimize Vue apps](#how-web-workers-help-optimize-vue-apps)
*   [Using web workers in a demo Vue app](#using-web-workers-demo-vue-app)
*   [Web workers and browser compatibility](#web-workers-browser-compatibility)

You can check out the full code for our [demo Vue app using web workers on GitHub](https://github.com/oyedeletemitope/vue-web-workers).

## Understanding web workers <a name="understanding-web-workers">

JavaScript, by default, is single-threaded. This means there's only one thread or single part, and this thread is known as the main thread. The main thread executes all the JavaScript for a webpage one line at a time. 

A web worker is a separate JavaScript process that runs in the background of a web page and allows you to execute multiple threads of JavaScript in parallel with each other. 

Instead of the main thread working by itself, you can use a web worker to offload any computationally expensive work. This means the main thread doesn't get bogged down and can continue executing some other code. 

One main difference between the main thread and the web worker thread is that web workers [cannot manipulate the DOM](https://blog.logrocket.com/how-the-virtual-dom-works-in-vue-js/). Only the main thread can access and manipulate the DOM. 

This distinction is important to note as it promotes better architectural decisions, improves application performance, enhances security, and contributes to a more seamless and reliable user experience in web development.

## How web workers can help optimize Vue apps <a name="how-web-workers-help-optimize-vue-apps">

Web workers have the potential to revolutionize Vue.js app optimization. They provide numerous advantages that lead to a better user experience, such as:

*   **Improved performance**: One of the core advantages of web workers is their ability to offload CPU-intensive tasks from the main thread. Delegating heavy calculations and data processing to web workers frees up the main thread for UI updates, resulting in smoother interactions and faster rendering
*   **Increased responsiveness**: Web workers run independently of the main thread, allowing them to continue operating even when the main thread stops. This implies that even during moments of intense computation or data manipulation, your Vue app may stay responsive and user-friendly
*   **Reduced memory usage**: Web workers run in their own memory space, isolating them from the main thread and reducing the risk of memory leaks. This isolation helps maintain a stable memory footprint, leading to more efficient utilization of system resources

Now that we have a basic understanding of web workers, let’s move on to showcase a project utilizing web workers.

## Using web workers in a demo Vue app <a name="using-web-workers-demo-vue-app">

Let's create a project that demonstrates how web workers work. Using Vue.js, we'll create an application that fetches real-time cryptocurrency data, all while keeping the user interface responsive and smooth. 

The web workers will operate independently from the main thread. This will help to offload resource-intensive tasks like data fetching to background threads. This ensures that our app remains fast and interactive, even when handling complex operations.

### Project prerequisites

To follow along with this project, you must have:

*   **Basic JavaScript knowledge**: Familiarity with JavaScript fundamentals will be beneficial as we explore the concepts behind web workers and how to integrate them into Vue.js applications
*   **Understanding of Vue.js:** A basic understanding of Vue.js and its core concepts, such as components and reactivity, will help you follow along and grasp the interactions between web workers and your Vue app

Let’s get started.

### Creating our Vue app

Open up the terminal, navigate to the folder where you want to install the project, and run this command:

```bash
npm create vue@latest
```

You’ll be prompted to name your project. I'll name mine `vue-workers`, but you can name yours whatever you want. Follow the prompts and instructions when installing. When you're done, change the directory to the just installed project and then run the following command:

```bash
npm install
```

This will install all the dependencies needed. Let's do a little cleanup. Navigate to `src/app.vue` and delete unnecessary code in our `<template>`. It should now look like this:

```javascript
<script>
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
</template>
```

We’ll come back to the `<template>` later.

### Fetching cryptocurrency data using web workers

We'll be fetching cryptocurrency data from [Coin Gecko's Public API](https://www.coingecko.com/en/api/documentation) into our Vue app. This API shows the names of trending crypto coins, their logos, and their price. 

Inside our `src` folder, create a file called `worker.js` and write a function that fetches the cryptocurrency data from the API, maps it, and sends it back to the main thread:

```javascript
function fetchCoins() {
 fetch('https://api.coingecko.com/api/v3/search/trending')
   .then((res) => res.json())
   .then((data) => {
     const coins = data.coins.map((coin) => ({
       name: coin.item.name,
       price: coin.item.price_btc,
       logo: coin.item.large
     }))
     console.log(coins)
     // Post the result (coins data) back to the main thread
     self.postMessage(coins)
   })
   .catch((err) => console.log(err))
}
```

In the code above, the `fetchCoins` function fetches cryptocurrency data from an API, processes the data into a simplified format, and then sends the processed data back to the main thread for further use in the Vue component. 

To ensure that the data remains up-to-date, [we’ll use `setInterval`](https://blog.logrocket.com/escape-memory-leaks-javascript/#forgotten-timers) to repeatedly call the `fetchCoins()` function every five seconds. Add this right after the `fetchCoins()` code:

```javascript
// Fetch data every 5 seconds
setInterval(fetchCoins, 5000)
```

This periodic fetching ensures that the Vue app receives continuous updates without user interaction. 

Lastly, in our `worker.js` file, we'll set up an event listener in a web worker. When a message is received from the main thread, the function `fetchCoins()` is invoked. Right on top of the other code in this file, add the following:

```javascript
self.onmessage = function () {
 fetchCoins()
}
```

So that's it for our `worker.js` file. Let's move to the `app.vue` file still in our `src` folder. Inside the `<script>` tag, import composition functions and [lifecycle hooks](https://blog.logrocket.com/introduction-vue-lifecycle-hooks/) along with our web worker file:

```javascript
import { ref, onMounted, onBeforeUnmount } from 'vue';
import Worker from './worker?worker';
```

We will use the composition functions and lifecycle hooks to enhance and manage the behavior of components. 

Let's define our component's behavior and setup. Paste the code below right after our imported web worker inside our `<script>`:

```javascript
export default {
  setup() {
    const coins = ref([]) 

    return { coins }
  }
}
```

Next, we need to define our `fetch` function. Inside our `setup()` function, after `const coin`, paste this:

```javascript
const fetchCoins = () => {
  fetch('https://api.coingecko.com/api/v3/search/trending')
    .then((res) => res.json())
    .then((data) => {
      const mappedCoins = data.coins.map((coin) => ({
        name: coin.item.name,
        symbol: coin.item.symbol,
        price: coin.item.price_btc,
        logo: coin.item.large
      }))
      coins.value = mappedCoins // Update the coins array
    })
    .catch((err) => console.error(err))
}
```

This function is responsible for fetching cryptocurrency data from the API. It uses the `fetch` function to make a GET request to the specified API endpoint. Once the data is retrieved, it is converted to JSON format using `.json()`. 

The fetched data is then mapped into a simplified format — an array of objects with `name`, `symbol`, `price`, and `logo` properties — and assigned to the `coins.value` property. 

We then need to initialize our web worker. Right after our `fetchCoins` function, paste this:

```javascript
const worker = new Worker()
worker.onmessage = (e) => {
  coins.value = e.data 
}
```

Here, we create a new web worker using the `Worker` constructor. The `worker.onmessage` event listener is set up to handle messages sent from the web worker. 

When the worker sends a message — which happens periodically, as seen in the web worker code — the event handler updates the `coins.value` with the data received, keeping the Vue component's data in sync with the web worker. 

Almost done. Let's paste this right after our web worker code:

```javascript
onMounted(() => {
     fetchCoins()
     worker.postMessage('start') // Start the worker
   })
   // Terminate the web worker when the component is unmounted
   onBeforeUnmount(() => {
     worker.terminate()
   })
```

The above code manages the behavior of a web worker, ensuring that the web worker fetches cryptocurrency data and starts its task when the component is mounted. It then terminates the web worker when the component is about to be unmounted. 

Lastly, we'll add this inside our `<template>`:

```javascript
<div>
   <h1>Trending</h1>
   <div class="container">
     <div v-for="coin in coins" :key="coin.name">
       <h2>{{ coin.name }}</h2>
       <p>{{ coin.symbol }}</p>
       <p>{{ coin.price }} BTC</p>
       <img :src="coin.logo" :alt="coin.name" />
     </div>
   </div>
 </div>
```

Congratulations! You've successfully created a Vue.js app that utilizes web workers to fetch cryptocurrency data in the background, enhancing performance and responsiveness. 

Checking the results in your browser, you should see the following: ![Browser Shown Open To Localhost 5174 Displaying Final Vue Project Using Web Workers With The Coingecko Api To Display Trending Crypto Coins And Values. User Shown Opening Developer Console While Using App To Demonstrate Web Workers In Action](https://blog.logrocket.com/wp-content/uploads/2023/09/Final-Vue-project-using-web-workers-CoinGecko-API.gif) 

With that, our project is done. You can [check out the full code on GitHub](https://github.com/oyedeletemitope/vue-web-workers).

## Web workers and browser compatibility <a name="web-workers-browser-compatibility">

Keep in mind that web workers are not yet available to use globally. Here’s a chart showing its availability as of this article’s publication: ![Screenshot Of Can I Use Website Showing Browser Compatibility For Web Workers](https://blog.logrocket.com/wp-content/uploads/2023/09/Web-workers-browser-compatibility.png) Make sure to check the [most updated information on CanIUse](https://caniuse.com/webworkers). It’s a good practice to have a fallback mechanism just in case a user’s browser does not support web workers, like so:

```javascript
    // confirm if the browser supports web workers
          if (typeof Worker !== "undefined") {
            const worker = new Worker("worker.js"); 
            // Set up web worker tasks and communication here
          } else {
            // Fallback mechanism for browsers without web worker support
          }
```

The fallback mechanism directs the app to go back to executing tasks on the main thread if the user's browser does not support web workers. This ensures that the application continues to run as intended in such cases — although somewhat less responsively. 

Alternatively, you can use libraries like `worker-loader`, `comlink`, and `workerize-loader` to emulate web worker behavior in unsupported browsers.

## Conclusion

In this article, we looked at what web workers are and also built a sample project to showcase how we can optimize our Vue apps with web workers. 

Using web workers to optimize Vue.js apps enhances performance and responsiveness. You can improve UX by offloading CPU-intensive operations, assuring responsiveness even during heavy workloads, and managing memory consumption more efficiently by leveraging the capabilities of background threads. 

Web workers provide a comprehensive toolbox to elevate your Vue.js project's performance and UX. Whether you’re using them for image processing, file uploading, data fetching, animations, or calculations, leveraging web workers in your Vue app is a great performance optimization tool.

---

## Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, [try LogRocket](https://lp.logrocket.com/blg/vue-signup).

[![LogRocket Signup](https://files.readme.io/00591d0-687474703a2f2f692e696d6775722e636f6d2f6a3049327856572e706e67.png)](https://lp.logrocket.com/blg/vue-signup)

[LogRocket](https://lp.logrocket.com/blg/vue-signup) is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - [Start monitoring for free](https://lp.logrocket.com/blg/vue-signup).