Understanding Nuxt & Vue hooks and lifecycle (part 2)
Tags: vue, nuxt, javascript
Published: 2019-07-16 12:12:02

---
title: Understanding Nuxt & Vue hooks and lifecycle (part 2)
published: true
tags: vuejs,nuxt,javascript
canonical_url: https://tech.onestopbeauty.online/front-end/understanding-nuxt-vue-hooks-and-lifecycle-part2/
---

This is part 2 of mini-series - Understanding Nuxt & Vue hooks and lifecycle. You can start with [Part 1 here](https://dev.to/lilianaziolek/understanding-nuxt-vue-hooks-and-lifecycle-part-1-48lc), to make sure you are at least vaguely familiar with most of the required concepts. If you have other programming background, but not in Vue/Nuxt, you might also find [my other post](https://dev.to/lilianaziolek/a-super-quick-guide-to-vuejs-ecosystem-from-senior-java-dev-point-of-view-1kmo) useful.

# What’s in the app

The [sample code](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples) contains very simple examples of all the mechanisms/hooks discussed in Part 1. For this article to make sense, especially “How it all works” section, you will need to **download it and run locally** to follow along.

Noteworthy files:

- [LinksComponent.vue](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/components/LinksComponent.vue)
  - Contains a (hardcoded) list of various links in the project to allow user-navigation.
  - Includes **mixin** [logRouteQueryAndParams.js](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/mixins/logRouteQueryAndParams.js). It demonstrates that what’s in mixin (computed property _routeParams_) is executed in a same way as if it was directly defined in the component, and that mixin code has access to _this_.
  - Shows most of the Vue component lifecycle methods
- [globalMiddleware.js](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/middleware/globalMiddleware.js) and [localMiddleware.js](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/middleware/localMiddleware.js) - as the names suggest, global middleware is attached from `nuxt.config.js` and thus executed before every route, whereas local middleware is only included for `test1/_param1/_param2` route.
- A few routes (pages):
  - [index.vue](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/pages/index.vue) - the starting point, contains LinksComponent
  - `test1/param1?/param2?` - a route with two optional parameters, meaning that `test1/`, `test1/lorem` and `test1/lorem/ipsum` all would land on the page generated by code in [\_param2.vue](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/pages/test1/_param1/_param2.vue) file
  - `test1/param1?/param2?` - a route equivalent to test1 route, it shows that if you don’t like naming your vue files with the name of last parameter to the route, you can name place them in subdirectory and name them [index.vue](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/pages/test2/_param1/_param2/index.vue) instead
  - `foo/x/_id?` and `foo/y/_id?` - shows how dynamic nested routes work. A nested route is where a page contains another `router-view` component, like in [foo.vue](https://github.com/lilianaziolek/blog-examples/blob/master/dry-examples/pages/foo.vue). You always get one by default with Nuxt (you don’t explicitly include it, Nuxt does it for you), so this is effectively a router-inside-router. Hence the name, **nested**.

# How does it all work?

Let’s assume the user first navigates to our main page (e.g. [http://localhost:3000](http://localhost:3000)) followed by navigating to various other pages, by clicking appropriate links. You need to click on the links rather than put URLs directly in the browser if you want to observe SPA mode in action. This is because navigating from address bar would force SSR mode.

Let’s take a look at an example user journey:

## (first visit) [http://localhost:3000](http://localhost:3000)

### What’s in the logs?

_On the server, before answer returned to client:_

```
(AlternativeEventBus Plugin) SSR: true inject component with id: 4
(NuxtServerInit) SSR: true
(Global Middleware) SSR: true
(LinksComponent) SSR: true [BeforeCreate]
(LinksComponent) SSR: true [Created] SampleProp: Prop from main page, SampleData: Lorem Ipsum Data
(LinksComponent) Created Refs:

```

_On the client (browser) side:_

```
(EventBus Plugin) SSR: false inject component with id: 1
(AlternativeEventBus Plugin) SSR: false inject component with id: 2
(LinksComponent) SSR: false [BeforeCreate]
(LinksComponent) SSR: false [Created] SampleProp: Prop from main page, SampleData: Lorem Ipsum Data
(LinksComponent) Created Refs:
(LinksComponent) SSR: false [Mounted] SampleProp: Prop from main page, SampleData: Lorem Ipsum Data
(LinksComponent) Mounted Refs: Foo With No Params,Foo X With Param1,(...)

```

### What just happened?

- globalMiddleware is only executed in SSR in this call
- _AlternativeEventBus Plugin_ is setup on both sides (client and server)
- _EventBus Plugin_ is only setup on the client
- beforeCreate and created are called on both server and client
- Mounted is only called on the client
- this.$refs are only populated in Mounted

### Where did we go wrong?

Imagine you have code somewhere in middleware or fetch and you register `this.$eventBus.$on` event listener. Then, based on some user interaction, you dispatch an event via `this.$eventBus.$emit`. Things don’t work, listener is not called - why?

As you might notice, _AlternativeEventBus Plugin_ **id** is different on client and server (if this is not the case for you, refresh the page, as ID on the server will change on subsequent SSR calls). That’s because this plugin’s code is executed on both client and server, and **both sides create an object**. Middleware and fetch are only executed in SRR on first call, so your **listener** is registered on the SSR instance of eventBus. The client-interaction code runs in browser, so your **emit event** triggers on the client-side instance of eventBus. Not the same instance - communication does not happen.

The main usage for eventBus is for one part of the code to notify another that something happened. This allows us to write a **more decoupled application**. For example your login code could publish an event that a user has just logged in, so that other parts of code can react and fetch extra user data into VueX. This way login code itself does not need to know anything about user data required in other parts of the app.

Therefore, in reality, such eventBus plugin may not make sense in dual (SSR/client) mode. If interaction always happens in the browser, it makes more sense to make such plugin client-side only. This way, if somebody tries to register a listener to event in SSR code, they will get a nice error saying that eventBus is undefined - rather than allowing them to register on an instance that will never receive any events.

## Click on link `Test1 with Param 1`

### What’s in the logs?

_In this, and all the following calls everything happens on the client (browser) side only :_

```
(Global Middleware) SSR: false
(Local Middleware) SSR: false
(Mixin) /test1/val1AsyncData: {"param1":"val1"}
(Mixin) /test1/val1Fetch: {"param1":"val1"}
(LinksComponent) SSR: false [BeforeCreate]
(LinksComponent) SSR: false [Created] SampleProp: Test1, SampleData: Lorem Ipsum Data
(LinksComponent) Created Refs: 
(LinksComponent) SSR: false [Mounted] SampleProp: Test1, SampleData: Lorem Ipsum Data
(LinksComponent) Mounted Refs: Foo With No Params,Foo X With Param1,(...)

```

### What just happened?

- Global Middleware, and now also local middleware, are processed on the client.
- Mixin code from _logRouteQueryAndParams_ for fetch and asyncData is now called
- all Vue lifecycle hooks from LinksComponent are called again. The route has changed and the instance of LinksComponent that was used in index.vue would now be destroyed and a new one (for test1 route) created

### Where did we go wrong?

Fetch and asyncData was not called on home page but it was on this page, why? That’s because index.vue does not include it as mixin, and \_param2.vue does. LinksComponent does contain this mixin too, but **asyncData and fetch are not called for components**. If you have a situation where your data does not seem to populate your UI, always double check if your fetching code is in a page, not in a component.

## Click on link `Test2 with Param1/Param2`

### What’s in the logs?

```
(Global Middleware) SSR: false
(Mixin) /test2/val1/val2AsyncData: {"param1":"val1","param2":"val2"}
(Mixin) /test2/val1/val2Fetch: {"param1":"val1","param2":"val2"}
(LinksComponent) SSR: false [BeforeCreate]
(LinksComponent) SSR: false [Created] SampleProp: Test32, SampleData: Lorem Ipsum Data
(LinksComponent) Created Refs: 
(LinksComponent) SSR: false [Mounted] SampleProp: Test2, SampleData: Lorem Ipsum Data
(LinksComponent) Mounted Refs: Foo With No Params,Foo X With Param1,(...)

```

### What just happened?

- Global Middleware is processed on the client. Local is not as it was not attached to this route.
- Mixin code from _logRouteQueryAndParams_ for fetch and asyncData is now called.
- all Vue lifecycle hooks from LinksComponent are called

## Click on link `Foo X with Param1`

### What’s in the logs?

```
(Global Middleware) SSR: false
(Mixin) /foo/x/val1AsyncData: {"id":"val1"}
(Mixin) /foo/x/val1Fetch: {"id":"val1"}
(Mixin) /foo/x/val1AsyncData: {"id":"val1"}
(Mixin) /foo/x/val1Fetch: {"id":"val1"}
(LinksComponent) SSR: false [BeforeCreate]
(LinksComponent) SSR: false [Created] SampleProp: SampleProp from Foo, SampleData: Lorem Ipsum Data
(LinksComponent) Created Refs: 
(LinksComponent) SSR: false [Mounted] SampleProp: SampleProp from Foo, SampleData: Lorem Ipsum Data
(LinksComponent) Mounted Refs: Foo With No Params,Foo X With Param1,(...)

```

### What just happened?

- Global Middleware is processed on the client.
- Mixin code from _logRouteQueryAndParams_ for fetch and asyncData is now called - TWICE! This is because both `foo.vue`, and `foo/x/_id.vue` include the mixin, and both are pages. In reality, you wouldn’t have the same fetch (from mixin) included in parent and nested route, so the fetch/asyncData would not be doing the same thing.
- all Vue lifecycle hooks from LinksComponent are called

## Click on link `Foo Y with Param2`

### What’s in the logs?

```
(Global Middleware) SSR: false
(Mixin) /foo/y/val1AsyncData: {"id":"val1"}
(Mixin) /foo/y/val1Fetch: {"id":"val1"}

```

### What just happened?

- Oh dear! Why is this output so different than for Foo X? This is because we are navigating within a **nested route** now. The app is smart enough to know that the shell (`foo.vue`) has not changed between `foo/x/val1` and `foo/y/val1` - it’s only the nested part (`x/_id.vue` vs `y/_id.vue`) that has changed. Therefore, there is no point regenerating anything related to foo.vue. We only execute what’s specific to y/\_id.vue - and this file does not contain a separate LinksComponent, so does not run its lifecycle methods.
- Global Middleware is still processed on the client.
- Mixin code from _logRouteQueryAndParams_ for fetch and asyncData is now called - but only for foo/y/\_id.vue

### Where did we go wrong?

We completely misunderstood/didn’t even read what nested components are, so at one point we had a structure like in foo route, but foo.vue page did not include `<router-view>`. The routing was working fine, but the fetch was then only called for route change - not params change. For example, if you went from `/foo/x/1` to `/foo/x/2` - the fetch for _/foo/x/2_ would not be called. But if you went from `/foo/x/1` to `/test1` and then to `/foo/x/2`, then fetch is called.

If you are in similar situation, and for some reason you actually need to make some changes in foo.vue data, then your best option is to add watch on route, i.e.:

```
watch: {
    '$route'(to, from) {
        // whatever you need to refresh goes here
        // you can get route (URL) params and query arguments before and after from `to` and `from` method parameters
    }
}

```

# Play yourself!

I hope the above go-through makes sense - but nothing will be as enlightening as taking this example project and playing with it yourself. Add hooks, extend existing code, navigate through the app and observe what happens. Let me know if anything is unclear!

In the last part, coming up soon, I’ll summarise both parts with a short neat table - stay tuned!

UPDATE: [Part 3](https://dev.to/lilianaziolek/understanding-nuxt-vue-hooks-and-lifecycle-part-3-8fo)