DEV Community

Lili Z
Lili Z

Posted on • Originally published at tech.onestopbeauty.online on

Understanding Nuxt & Vue hooks and lifecycle (part 1)

Remember, Young Padawan: DRY

One of the software development principles that we get taught very early in our dev careers is DRY - Don’t Repeat Yourself. It’s a good thing too, as there isn’t much worse than trying to crawl through a massive codebase trying to find all the copy-pasted instances of the same piece of logic.

When we first started with Vue (and later Nuxt) I wasn’t always sure where to put certain bits of code, like getting data from the server, or checking if the user is logged in. Enter: the topic of this mini series. I will start with a quick recap of what mechanisms are available in Vue/Nuxt landscapes, I’ll follow with examples of situations when each of these might be useful, I’ll point out places where we got it wrong so that you don’t have to, and summarise the whole thing with a little reference table.

One of the trickiest aspects was to reconcile how the situation varies between SSR and client-side, and there were a few cases we had to work out why things would work on refresh but not route change, or vice-versa. We sometimes got it wrong when various hooks/methods get called and, more importantly, when they don’t get called. The information is usually somewhere in the docs (plus, the documentation has massively improved in the last year or so) - but I think it’s nice to have it collected all in one place.

Recap: Vue lifecycle

Vue documentation has an excellent diagram showing the order/situations in which Vue component methods are called. Unfortunately, it does not clearly mention a few important things (as it’s related more to how Nuxt operates in universal mode, than to pure Vue).

  • Only beforeCreate and created are called during SSR (as well as on the client). All the other methods (most importantly: mounted, which is one used quite often in examples) are only called on the client. So, if you have a piece of logic that needs to be executed during SSR, mounted (which, otherwise, is often a good place for some extra logic) is not a good place for this.
  • beforeCreate does not have access to the components props/data because this (the component reference) is still undefined.
  • created, does have access to this , including data and props, but does not have access to DOM. How does it matter? If you ever want to use e.g. this.$refs, they are not initialised at this point. They will only be processed (visible) in mounted. Which does not run in SSR.

Recap: Nuxt-specific tools

Note: many of the following methods accept Nuxt context as one of the parameters.

Plugins

Plugins are bits of code that get executed once or twice per visitor, before Vue.js app instance gets created. You can have plugin that’s executed on both server and client side (thus twice total), or only on one side. Nuxt has useful convention that any plugin called XXX.client.js get executed only on client side, and YYY.server.js is only in SSR. Additionally, Nuxt makes available an inject method that allows you to make shared code/functionality available in vue instances/components, Nuxt context, and/or VueX store. A popular plugin is Axios, which allows you to access a shared Axios instance e.g. via this.$axios. Similarly, you can create your own plugin and access it, e.g. via this.$eventBus.

Modules

A module code is executed on Nuxt startup (i.e. once during the lifetime of your Node.js server ). Modules extend nuxt functionality - for example they can automatically add and configure a plugin. It is NOT executed in browser/on each page, or even on the server for each client accessing your page. Therefore, modules are not a good place for any code that should be executed for each visitor. Unless, of course, your Nuxt module adds code into one of the hooks that do get executed for each visitor - but the module code itself would run just once, to initialise certain hooks.

nuxtServerInit in store/index.js

One of the first actions executed in SSR (only) is nuxtServerInit. It is executed just once for each visitor to your website (when they first navigate to your website, or when they hit refresh). It is a good place to put Axios calls to get some commonly used data and put it in store.

Middleware

Middleware is executed before rendering each page (before route is loaded), regardless if you’re on server- or client-side. You can have global middleware (configured in nuxt.config.js), or localised middleware, attached only to certain layouts and/or pages. It’s important to know that middleware is only executed once before render - i.e. on first hit to the page it will be executed in SSR only. On subsequent pages/routes it will be executed on the client only. It does not get called on both client and server for the same page.

Mixins

Mixins are extensions to components, pages, or layouts. They have access to the whole component that they are mixed into - so they can use this.$route, this.$store, and anything else you’d be able to call in the component. They are very useful to extract common functionality (including hooks like mounted) that for some reason cannot be extracted as standalone components. In simple terms, they behave in the same way as if you had copy-pasted the mixin code into each component where it’s used.

asyncData & fetch

Both asyncData and fetch methods are executed before the component is initialised and so do not have access to this. Both can be used to get some data from an API to populate the component. Both are *only executed for pages (NOT components). Both take Nuxt context as parameter. Both will be executed on server-side on first load, and on client side for subsequent route changes. (Note: there are some subtle caveats here as to when these are called or not which I’ll get into in a separate post)

  • asyncData should return a promise, or use async/await - but in either case, the result returned will be integrated into data part of the component
  • fetch , on the other hand, should be used for data intended for VueX store - it does not need to return anything and should instead commit to store any required data. It can use async/await.

Bonus: watch route

None of asyncData or fetch get triggered in some specific situations when only route params change. For this situation, you may need to watch the route to refresh data - or change your router configuration. More details in a separate post.

Official Nuxt documentation has a useful diagram showing the order in which things are called. Let’s go into a bit more details into what it means in relation to typical user-app interaction.

Example

The code for this post (and all the more detailed follow-ups in this series) can be found on Github.

In the next post (or a few) in this series, I’ll go through what exactly happens as the user navigates through the app, and will point out various techniques and gotchas related to the tools covered above.

UPDATE: Part 2 is now live.
Part 3

Top comments (17)

Collapse
 
kp profile image
KP

Hi Lili, sounds like you have been doing a lot of Vue / Nuxt / SSR stuff so you may be the right person to ask. I'd really appreciate your quick 2-second take on this.

I am thinking of creating a site like dev.to for a different focus area (a dynamic site with user-generated content, not a static site hosted on netlify). I love how snappy dev.to site is, every page loads so fast.
My back-end is currently in Laravel 5.8, and I don't intend to change that because I've spent the last 3 yrs learning Laravel. However, I am trying to get to a SSR or server-side-rendered app (as against the slow client-server default app). I care about speed (just like dev.to) and SEO is important also because of UGC. Which of these would you recommend if starting with Laravel and vue right now?

  1. Vue integrated into Laravel app: Is using github.com/cretueusebiu/laravel-vu... the right way to go?
  2. Vue and Laravel in separate folders: Vue (as a SPA) makes API requests to laravel....but SEO will be a problem right?
  3. Is nuxt.js right for me? Can nuxt even be used for complex dynamic sites?

What tech stack / architecture would you use? Appreciate your thoughts!

Collapse
 
lilianaziolek profile image
Lili Z

Hi KP!
Our page is actually also UGC so I know exactly your thoughts / concerns!
Let me start by saying that apart having heard the name, I know VERY little about Laravel - but I do know it's often used with Vue, and I'm assuming the capabilities are roughly equivalent to any other backend-server, i.e. amongst other things, you can expose REST APIs out of it.
If that's the case, then we actually went through all 3 options with OSBO :)
We started with 1 - in our case, it was Java Spring Boot webapp, and Vue was embedded within just by importing from CDN, and later we also hosted the libraries from webapp. This I would only recommend for a very, very light touch website. You lose ability to package your app in a sensible way (it will be slower as you can't easily to code splitting, or otherwise use webpack), you start having messy code, and managing dependencies can be a nightmare. No helpful resources, as not many people do it this way. Overall, I'd strongly recommend against route 1. The familiarity of your existing stack will be tempting, but in the long run, it's a waste of time IMO (except, as said, with very light/static usage of Vue).

We moved on to 2, that is, a separate (plain) Vue app in Node.js server and Java backend with REST APIs. This is a huge improvement. The pros are that you are already in the land of "common standard", plenty of resources, cleaner code. The main con is that you now have to run two services. At the moment we run things on GCP and that means managing two deployments, two services (and paying for them) etc. But there are ways to cut the cost, if that's a main concern (it will be for us soon). Other cons are that you still have quite a bit of integrating tools on your hands (e.g. VueX, Vue Router, Vuetify - or whichever components framework you use) and, most importantly, SSR. You are right that SEO hints at using SSR, especially for sites that are not super-simple (google bots manage to process a bit of JavaScript - but you don't want them to process ALL of your content, it didn't work for us very well). You actually can do SSR with plain Vue, but in our case, that seemed so complex that this was exactly the point when we moved from route 2 to route 3.

Route 3 is, IMO, the absolutely best for complex Vue projects. It comes with an awful lot "out-of-the-box", and once you understand the SSR mode, it's actually really easy to work with. It takes some time, and I'm trying to write this up as I didn't find as many resources in this topic (apart from simple tutorials) as I'd like to - but... yeah. From what you said, your app seems quite similar in terms of architectural needs to ours, and Nuxt works really, really well for us.

Take a quick look at my guide to VueJS post - it's written from Java devs perspective, but I think the explanation of various Nuxt modes can be helpful for you regardless.

Any other questions, feel free to ask! :)

Collapse
 
kp profile image
KP

H Lili, phenomenal answer, thanks for taking the time to write this. It really helps (and I havent found a half-decent explanation anywhere). Even the Nuxt introductory video (why use Nuxt?) at nuxtjs.org/ is wayyy too complicated for a beginner to understand.
I did take a look at your post on the VueJs ecosystem: dev.to/lilianaziolek/a-super-quick... which is phenomenal in it's own right.
If I want to create a complex social networking app (ala dev.to or Linkedin), it sounds like "universal mode" is the way to go for me....is that correct?

Yes, Laravel is a back-end framework with powerful capabilities like APIs and lots of powerful features and integrations.

  1. Do Nuxt and Laravel need to live in separate folders as separate services? The "common standard" point you made makes a lot of sense.
  2. Sadly, I currently have some code doing #1, some doing #2 (the good news is there isn't a lot of code, I am only just getting started). Is there a guide on migrating from #1 and #2 into #3? Any resources for getting up to speed with Nuxt quickly would be appreciated (there's too much information to wade through these days.....I want to make sure I'm using the right content).

I really appreciate your perspective.

Thread Thread
 
lilianaziolek profile image
Lili Z

You're very welcome :)
Yes, IMVHO in Vue ecosystem, for any more complex applications, Nuxt in universal mode is the best thing since sliced bread ;)

As for your other questions:

  1. Yes, in route 3 (and 2 really) they will both have their own little worlds. In our case, since we haven't grown too big yet, for now it is a single git repo, with subfolders for java/python/vue, and then further subfolders for projects. You could also create completely separate git repos - that is down to you, how big your team is (if any), and personal preference to some degree.

  2. I don't think I came across any significant guide for this purpose - and since I just started blogging, I don't have our journey documented in any sensible manner. :( The good news is that, unless you have loads of code already (we had...) the migration should be relatively straightforward as it will be mostly moving files around (Nuxt requires you to put things in certain places) - but should not be a complete re-write.

Nuxt has a good "starter" project, so I'd recommend kicking it off from there, there is a route or two as an example.
One concept that, IIRC, is new to Nuxt vs plain Vue is the concept of pages vs components. Basically, pages are what gets automatically translated to Vue Router routes (if you used router already). Components, on the other hand, are the smaller building blocks. So whatever .vue files you now have, if it's mapped to Vue router route you'd move and name appropriately into /pages directory, and the rest would go into components.

Also, if you're using VueX, then there is a standardised structure for organising modules, and I'd recommend sticking to this instead of manually configuring VueX.

Finally, in our case the biggest stumbling block when decoupling from 1st way (all-in-one) was authentication/authorisation. I am a strong believer in backend driven security (i.e. "never trust the client") and I wanted to keep using Spring Security for this purpose. That was one of the most frustrating aspects of integrating the two technologies and I do have a blog post in plans for this, too. So, if you want a good (risky) place to start, I'd probably start from login and some basic authenticated user page. Once you have this working, all else will seem trivial in comparison ;)

Overall, if you have any specific questions (how to do X), feel free to shout. I have loads of posts in plans, but if I know there is interest in specific topic, I may prioritize it over some other less interesting/better covered topics.

Thread Thread
 
kp profile image
KP • Edited

Lili, I am so appreciative of your response! From both your posts and your detailed responses, one can tell that you have a ton of experience. Thank you for sharing your knowledge and expertise.

  1. Like you, I will keep things in one git repo for now, until I outgrow it. God knows that nowadays code is complex as-it-is (it's amazing how much things have changed in a few short years...it seems that the pace of new technology is only accelerating, making everything a moving target). So for now, I am starting with github.com/cretueusebiu/laravel-nuxt which seems to work out of the box. Now if only I understood how every piece worked :)
  2. On #2, you're right, as far as migration guides are concerned, there isn't a lot I can find either.

I just installed the nuxt starter project. I also found myself nodding my head silently in agreement to your points about:

  1. decoupling authentication/authorization
  2. backend driven security I would love to read the blog posts you have planned on these topics. In the meantime, if you happen to have any links to code in nuxt that properly decouples authentication/authorization, that would be useful.

I'm already following your updates. If you follow me back we can connect / message each other. Love to chat further and help you with OSBO if possible. I'm good on the business side.

Thread Thread
 
lilianaziolek profile image
Lili Z

Hmmmm, I just took a look at your laravel-nuxt project and the project structure looks quite a bit different than my typical nuxt project (see one linked from this post as example). Is this what was generated by yarn create nuxt-app XXX ?) I can't see any recent commits though so hopefully it's just because it's not pushed yet!
Stay tuned, I'll put the auth post in line right after I finish this series.

Followed back, and happy to chat direct :) Would not mind if you have any hints on how to reach our target audience, haha! :)

Thread Thread
 
kp profile image
KP

Actually is the structure that different?
Compare the 'client' folder at github.com/cretueusebiu/laravel-nu...
with github.com/lilianaziolek/blog-exam...

Sounds good, look forward to reading more posts from you on this topic, and chatting 1:1 :)

Thread Thread
 
lilianaziolek profile image
Lili Z

Ah yes, client folder seems very similar. But it's inside of the other codebase, that's quite different. In my case spring boot app is in osbo/rest-api and nuxt is in osbo/nuxt - they are basically totally separate. That's what got me confused.

In this case, how would you typically build and deploy this to run? Do you just run two different scripts from the main folder, deploy two things and still end up with two separate services? Or does laravel end up hosting nuxt generated code?
Nuxt has "generate" option where you'd end up getting ready html/CSS/js to host, and if this code structure gives you that, and laravel then serves it, then it's not quite the same as option 3. And if you end up with two independent services, then whilst it can work from one folder, I'm not sure if I see the benefit of keeping it all in one project.

As said, I don't know much about laravel and how it's typically used with nuxt, so perhaps that's normal - but I just can't see a benefit of keeping these two protects blended into one if you're after the nuxt universal/two separate services route.

Thread Thread
 
kp profile image
KP • Edited

I can only speak to the development / local server, because that's all I have tested so far. Production might be more complicated, I'm not sure though.

  1. laravel-nuxt/ --> root folder, this has all the laravel code (folders like app, bootstrap, config, resources, etc). See github.com/cretueusebiu/laravel-nuxt
  2. laravel-nuxt/client --> this has all the nuxt.js code (typically the 'client' folder doesnt exist in a laravel app) See github.com/cretueusebiu/laravel-nuxt

To run this on my local / dev server, I basically need 2 commands, both run from the root laravel-nuxt/ folder:

  1. php artisan serve ---> laravel starts listening on port 8000. Laravel development server started: 127.0.0.1:8000 and hosts the APIs in app/Http/Controllers: github.com/cretueusebiu/laravel-nu...

  2. npm run dev --> this starts nuxt on localhost:3000.

OneStepAtATime:laravel-nuxt kp$ npm run dev

> @ dev /Users/kp/Code/LARAVEL_WORKING_CODE/NUXT/laravel-nuxt
> nuxt -c client/nuxt.config.js

  nuxt: Call build:before hooks (1) +0ms
  nuxt:build App root: /Users/kp/Code/LARAVEL_WORKING_CODE/NUXT/laravel-nuxt/client +0ms
  nuxt:build Generating /Users/kp/Code/LARAVEL_WORKING_CODE/NUXT/laravel-nuxt/.nuxt files... +0ms
  nuxt:build Generating files... +18ms
  nuxt:build Generating routes... +12ms
  nuxt:build Building files... +26ms
  nuxt:build Adding webpack middleware... +3s
  ████████████████████ 20% building modules{ parser: "babylon" } is deprecated; we now treat it as { parser: "babel" }.
  ████████████████████ 100% 

Build completed in 8.119s

 DONE  Compiled successfully in -2877ms                                                                                                                                                          12:46:55 AM

 OPEN  http://localhost:3000

You then go to localhost:3000 and it just works. Nuxt calls the Laravel APIs.

What do you think, given this description?

I'm not familiar with the nuxt "generate" option..so I am confused / curious what you think.
What is the benefit of the two separate services route?

Technically the 'client' folder could be external to the Laravel app (laravel-nuxt), and I have done exactly that in Laravel -> Vuejs SPA apps, but never with Nuxt. I just started with the codebase above. What is the drawback of having them all in one project?

ps. also left you a message

Thread Thread
 
lilianaziolek profile image
Lili Z

If you end up with two servers (Node.js running Nuxt/Vue on 3000 and Laravel server on 8000), then I think you are indeed on route 3 :)
Overall, you can keep it this way as it works, but there doesn't seem to be a requirement to keep it all in one folder. To a degree it's personal preference. I would/do keep things that run as separate processes and involve different tech in separate directories - just to make it easier for anybody looking into this code to see where the boundaries are (it also makes managing deployments easier for example). But, it's just a minor point, as long as you end up with these two servers running, all is fine and will work:)

Generate option: it's just an option in Nuxt to generate a static site. It will create a dist folder with everything (HTML/CSS/JS rather than .vue files) inside ready to be deployed.

Thread Thread
 
kp profile image
KP

That's good to know, Lili! I was worried that I was going down a wrong path! I agree with you, keeping the folders separate and managing the deployments separately makes a lot of sense. Right now, since it's just me and I don't have a team, I'm going to keep them all in one folder (though I may make the 'client' folder it's own git repo and the master repo include it as a git submodule: git-scm.com/book/en/v2/Git-Tools-S...)

Thread Thread
 
kp profile image
KP

One thing that is still confusing to me is why this repo has a router.js
github.com/cretueusebiu/laravel-nu...
I thought Nuxt automatically compiles the routes based on the files in the directory. Not sure if you have an idea what this is for...

Thread Thread
 
lilianaziolek profile image
Lili Z

Re: router.js - you are absolutely right, Nuxt will create router.js based on /pages folder structure (you can see this in sample project for this post too). Not sure why there is one in your source code... Maybe a leftover from another approach? Or maybe sth went wrong in code generation in Nuxt template? Either way, you should not need it, and I'm not sure what happens if you have it (will it override Nuxt, or just be ignored?).

Thread Thread
 
kp profile image
KP

Thanks Lili. Upon digging deeper this is what I found / filed: github.com/cretueusebiu/laravel-nu...
He is essentially overriding the default Nuxt functionality of auto-creating routes because he likes doing it the manual way. So yeah, I've asked him to remove it and he seems open to the idea.

Collapse
 
eyesurewould profile image
eyesurewould

Lili,

Great summary!

With Nuxt 2.12 (released in March 2020), the capabilities of the fetch method have changed (some would say improved), specifically the availability of this in the fetch method.

I learned about it here: nuxtjs.org/blog/understanding-how-...). Might be a great excuse to revisit this list and update the post.

Thanks,
Ian

Collapse
 
rahulitblog profile image
Rahul Kumar Singh

Hello Lili,

Recently I created Website using Nuxtjs, Everything is going fine after that i added dark mode in website and then the problem starting, I saved dark mode data in local storage and when i call it from default page it works but after some sec first show normal page and then it changed to dark mode.

Here is my code:

beforeMount() {
if (this.$ssrContext) return
this.$vuetify.theme.dark = localStorage.getItem('darkmode') === 'true'
},

After some time i know that beforeMount() is only working in client side that's why this happens.

I want to know what should i use to apply dark mode before render, In simple work i want to know alternative of beforeMount() in SSR

App Mode - Universal (Server Side Rendering)

Collapse
 
slaknoah profile image
Slaknoah

Have you tried using cookie instead ?
You might find this helpful
github.com/microcipcip/cookie-univ...