Skip to content

Instantly share code, notes, and snippets.

@CMQNordic
Last active May 26, 2022 08:38
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save CMQNordic/3a40aaa970dbddf48f23c44384162739 to your computer and use it in GitHub Desktop.
Save CMQNordic/3a40aaa970dbddf48f23c44384162739 to your computer and use it in GitHub Desktop.
Vue & Nuxt | Quickly refresh your skills | Written by Martin Czerwinski @ CMQ Nordic

Reference Guide - Vue & Nuxt

This document helps you to refresh your Vue skills and can also be used for quick look-ups. This reference guide was written with love and broken english, for myself, by myself as notes for myself, educational purposes and just for for fun.

(This document is not fully finished still work in progress)

Written by Martin Czerwinski ®CMQ Nordic AB | Feb 2021



Really good articles to read, understand and apply in daily work:



ECMAScript 7 is also known as ES7, ECMAScript 2016 or JavaScript 7. Not all browsers may support all ES7 syntax but we still can use the latest version of javascript in our projects as webpack usning babel that transform latest ECMA code into older javascript syntax.

Vue uses HTML-based template syntax that in background bounds to virtual DOM. Vue can intelligently figure out the minimal number of components to re-render and apply the minimal amount of DOM manipulations to present the changes in the broser UI.

We can use vue in two diffrent ways. by apply Vue code to our HTML code marked with id. We can target only parts of our HTML pages (kind of "vue-widgets" within existing HTML) or bind Vue to control whole HTML site (commonly as single page application). When controlling whole web page we use a building project and Vue CLI helps us to set up such a projects. Then the code we write is not the code that is run in browser. The code is compiled and bundled by webpack that is build in the projects created bu Vue CLI. The project structure is then set up and templates of main app and components are set up in .vue files.

ES6/7 syntax is applied as babel and webpack transforms the code to older js syntax when building the app. data: function() {...} can be written as data() {...})

Read more:
Learn basics of Vue 3 --> here
HTML DOM events ---> here
Vue 3 API --> here
Modifiers in Vue 3 ---> here
Event handling in Vue 3 ---> here

↩ Back To Top


React was released 2013 by Facebook and is mostly used in high traffic websites. Is is more like a library as it has less default build in features (for example routing witch vue offers is an external plugin in react). Whatsapp, Instagram Paypal, BBC are some of the popular companies use React.

Angular was released in 2010 by Google. It is a typescript based javascript framework. It is heavy javescript framework and have lots of default build in features. It is specially popular in enterprice world among big companies. Google and Wix are among the most popular companies use it.

Vue was released in 2014 and developed by no big name like React and Angular. It is a progressive meaning it can easily be integrated into an existing projects. It has practically removed the drawbacks of the other frameworks to offer ease of software development. From version 3 it supports typescript. Websites like GitLab and Alibaba are using Vue. At the moment Vue is very popular for fast prototyping. It has excellen documentation. It nis easy to get started with and it has an easy learning curve making it a cost-effective solution for small-scale to medium-scale apps.

↩ Back To Top

Vue 3 introduced a long list of changes and additions to the Vue framework. The most obvious is the way you create and load a Vue app. Vue 3 introduced the createApp() function that returns an app instance. It represents the app context and will be used to define plug-ins, components, directives, and other objects. In a nutshell, this app instance replaces the Vue instance in Vue 2. Moreover, Vue 3 brings over the new Composition API.

A vue plug-in is a self-contained code that adds a global-level functionality into your app. When you create it, you package it as an NPM package. Then, whenever you need this plug-in, you install it into your app to avoid repetition of components, directives, global functions, and the like. Examples:
Vuex, Router, Adding some global methods or properties, Adding global mixins (https://v3.vuejs.org/guide/mixins.html), Adding a library that provides an API.

So in order to make things global we create plugins:

↩ Back To Top

There are different ways to install Vue and create a Vue project.

↩ Back To Top

From HTML <script> element

Simplest way is to get started adding simple vue functionality (or just to learn Vue basics) is by including Vue with a <script> tag in the header of your html. Vue will then be registered as a global variable and you can access it from all your .js files and add Vue functionality to existing html project/file without any webpack bundler not any additional plugins. A disadvantage is that you can not use .vue file (Single File Component) as there is no bundler to pase it so you must add all you code and templates in jacascript files.

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>

↩ Back To Top

From npm packages

You can add Vue manually package by package through npm but be aware that then you must configure webpack well on your own (webpack.config.js) to build both for production and dev.

To get access to Single File Component (SFC) functionality you can to add Vue as a plugin and use it together with Webpack bundler to get your vue files parsed.

To install latest stable Vue version 3 run:

npm install vue@next  // vue core
npm install -D @vue/compiler-sfc  // dependency tool for parsing of .vue files
npm install -D vue-loader ///(loader@16.8.3 see note)

...
// many more plugins to add for full project setup

Note! Vue version 2 was paired with plugin vue-template-compiler. Those must (had to) always match. In vue version 3 vue-template-compiler was replaced with @vue/compiler-sfc though! Now latest version returned by vue@next must be matched with latest version returned by @vue/compiler-sfc`. If you install or update latest version at same time you are fine!

Note! There is a third component you must install for it to work and it is a loader for webpack bundler.

Note! At writing moment latest official version of vue-loader is 15.X. This version is still configured to work with Vue 2 and as Vue 2 is dependent on vue-template-compiler then it requires it. But from vue-loader versions 16+ it-s requires @vue/compiler-sfc instead. Therefore for installing vue-loader for Vue 3 run:

npm install -D vue-loader@16.8.3

↩ Back To Top

With Vue CLI

If you are not interested in manually setting up webpack configuration, the preferred way to create a vue project with predone configuration is to use Vue CLI. Also Vito do same thing. Nuxt goes one step further and sets up preconfiguration or Vue as well with preconfigured routing and simplifies getting started even more. Vue CLI use in background Webpack as default bundler, webpack-dev-server, postcss and autoprefixer. Vue CLI is a globally installed npm package. The CLI Service (@vue/cli-service) encapsulates all needed dependent plugins and configures them out of the box. One of the components of Vue CLI is @vue/compiler-sfc that parses .vue files.

node -v // current node version
vue --version  // currently vue CLI version

npm install -g @vue/cli // installs Vue CLI globally
npm update -g @vue/cli   // updattes vue cli

// update node
npm cache clean -f
npm update -g

Vue CLI plugins are npm packages (listed in package.json) that provide optional features to your Vue CLI projects. For example Babel script transpilation, ESLint integration, Unit testing and more. It's easy to spot a Vue CLI plugin as their names start with @vue/cli-plugin-XXX

Once you have Vue CLI globally installed on your machine you **scaffold a new project **with:

vue create <project-name>  // follow wizard in command prompt

or

vue ui  // use handy GUI wizard


npm list --depth=0  // see the versions of installed packages in packages.json

When choosing default CLI project setup then some default basic components like "babel" and "eslint" are included by default. But you can manually select features too. Diffrent components of a Vue CLI project. Through vui UI you have some more options to configure. Here is a list of functions:
  • Init git: Do create a local git repository automatically? (on by default)

  • Babel: Use script transpiler? It converts modern js into plain old ES5 JavaScript that can run in any browser

  • TypeScript: Use typescript? Strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.

  • Progressive WEB App (PWA): PWA use progressive enhancement and new capabilities that are enabled in modern browsers. Using service workers and a web app manifest the web application becomes installable. Installed Progressive Web Apps can be run in a standalone browser window instead of a browser tab. They're launchable from on the user's home screen, dock, taskbar or shelf.

  • Router: Use vue specific routing functionality?

  • CSS Pre-processors: PostCSS & Postcss-loader are included in all Vue CLI projects by default. For processing other CSS preprocessors like Sass or Less you must choose them to be added.

    To gets started with PostCSS you just need to install desired postcss plugins and define the usage of them. For example to use nesting within css code you must install a plugin named postcss-nested (run npm install -D postcss-nested) and then add a postcss.config.js file in the project root and in it instruct postcss-loader (that is by the way part of vue/cli-service) to use the postcss-nested plugin. This is done by adding by adding:

    // After installing plugin "postcss-nested" you must add it to postcss.config.js configuration file in a vue CLI project.
    
    __________________postcss.config.js______________________;
    module.exports = {
       plugins: [require('postcss-nested')()]
    };
  • Linter/Formatter: EsLint checks the syntax of your code and warns you or generate errors AND it additionally can formats your code. When setting up a project we are asked what linter style to use (error prevention, prettier, airbnb or other style guides). If you choose at project setup to use separate configuration files then a .eslintrc.js will be added in the root and contain configuration for eslint (otherwise the configuration will be nested in "package.json" file).

Note! By default Vue styling is added as "extends": "plugin:vue/vue3-essential" in all configurations!

Eslint has following default setup of rules for javascript ([click here](https://eslint.org/docs/rules/)) and those can be enabled in eslink setting with `"extends": "eslint:recommended"`. Here we have for example rule "no empty statements" so `func(a) {}` generates error but `func(a) {//empty}` is ok.

Airbnb are super trict rules where for example console.log() is not allowed, nor backticks or double quotes. [More here](https://github.com/airbnb/javascript)
  • Unit Testing: TODO
  • E2E Testing: TODO
  • Preset: Save this setup as template for later?

Finally to run our newly created Vue CLI project we must navigate into the project folder and from command line build the project and serve it at our local host with:

npm run serve

Note! There is not possible to add UI framework to use with Vue CLI projects at setup. For that Nuxt can be used where u can at project set up define to use UI component framework such as Bootstrap-Vue or Vuetify.

Note! Avoid &-characters in folder names in you projects as if absolute path to contains &-characters you might experience strange errors.

Read here how to install it and get started for Vue version 3.

↩ Back To Top

With Nuxt

npm init nuxt-app@latest <my-project>

You will be asked:

  • If use npm or yarn (npm)
  • UI framework to use i.e. Vuetify, Tailwind or Bootstrap-vue.
  • Use Axios (for HTTP requests) or add PWA support (apps that can be saved in browser).
  • Linting tools: EsLint or other like Prettier?
  • Testing tools.
  • Build as Universal App (process first request on the server and send content as html and afterward as single app all the rest as script), or as Single Page App (one-page website (empty html sent to client) with all the logic, pages and routing written and loaded as javascript. Desktop-application-feel. Once loaded, it’s faster. Slow to boot and bas SEO). Advantage with Universal app is that for first request the client will receive pre-made data and pre-rendered HTML/CSS, thus showing content faster to the user. As we receive rendered HTML that crawlers can work with improves SEO. These are are apps that can run on both server and client seamlessly and need to be deployed to a server with node.js hosting.

A cool feature of Nuxt is that it can generate a static version of your application which you can deploy without the need of a server. This means that you can host your application in a service like Netlify or GitHub pages which are actually free even when you have a ton of traffic. Single Page Apps can be deployed on static/JAMstack hosting such as Netlify, Firebase och Github hosting

Note! JAMstack sites are static sites built with HTML, JavaScript and CSS; they're text files, so they can be served up fast without requiring the overhead of traditional compiled or interpreted architectures. Jam stands for “JavaScript, APIs and Markup.”

  • Deployment tools. Configured in jsconfig or tools like Dependabot

↩ Back To Top

With Vito

TODO


↩ Back To Top

We can simplify writing of UI by using predefined standard UI elements and CSS frameworks. Using a UI framework with Vue is a great combination, because it enables developers to abstract common components providing a maintainable and productive development process. Most of these components have well written tests and are consistently optimized to be reactive to provide the best performance available.

Even though Vue 3 is ready for production at writing moment many UI frameworks have not finish adaption to Vue 3. For example both Bootstrap-Vue and vuetify are at writing moment not Vue 3 ready and in progress. This prevents many developers from jumping go over to vue 3.

Here are some popular frameworks to use in a Vue project:

  • Vuetify (450k weekly downloads). Utilise all API in VUE, Material Design and customizable.
  • Bootstrap-Vue (350k weekly downloads). Build on popular Bootstrap with javascript/jquery replaced by reactive vue code.
  • Quasar (62k weekly downloads). Build Vue user interfaces for SPA, PWA, SSR, mobile, and desktop. Packs in Cordova, Capacitor, and Electron, which can help you build desktop and mobile experiences without having to learn them individually.
  • Element Plus (31k weekly downloads). Brings to Vue 3 a large array of unobtrusive components. Most of what we need to create a very complex application is already made and ready for use.

At writing moment only Quasar and Element+ have Vue 3 support.

Bootstrap 5 no longer uses jQuery so it's easier to use with Vue therefore Vue projects and no longer requires a library like bootstrap-vue but can integrate Bootstrap 5 directly. Install bootstrap as you would any other JS module in the Vue project. Next, add the Bootstrap CSS and JS components to the Vue project entrypoint (usually) main.js.

npm install bootstrap

____________________________ main.js ___________________________
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap"


↩ Back To Top


ESLint complains about my end of line sequence
In VScode you can define what end of line sequence to use and it can be done by pressing small icon in in bottom bar (CRLF or LF). Most probably you want it to be CRLF and not LF. You can also add following line in your .... to eliminate this linting error.

Have you ever encountered following error when building you Vue CLI project?

Syntax Error: Error: PostCSS plugin postcss-nested requires PostCSS 8.

Sometimes added plugins (i.e. postcss-nested v. 5.06) are too new for our Vue CLI project setup. Vue CLI v.4 uses Webpack v.4 and therefore it uses a postcss-loader v.7. But if we install lasted 'postcss-nested' plugin (as now) it has version 5.06 and depends on postcss-loader v.8. Then you will get this error when building. Solution: sinmply upgrade you CLI prooject (if possible) or use older version of plugin.

How to check what version of Vue CLI, postCSS and postCSS-loader is used in my Vue CLI project?
Check package.json file for '@vue/cli-service'. search inside 'node_modules' directory for postcss and postcss-loader directories and in there you find their installed versions.

How to check what version a Vue plugin I want to install depends on? Search for the plugin on npmjs.com and if not found there from there navigate to its github repository. In repository look at package.json file (there dependencies are mentioned) and look at diffrent history versions till you find the version that matches you setup of project.


↩ Back To Top


A Vue instance is essentially a ViewModel as defined in the MVVM pattern, hence the variable name 'vm' you will see throughout the docs. We connect the Vue instance with a html with mount('#[id]') command in a .js file (usually named main.js). After that we can use Vue directives and defined parameters in that part of HTML code. When creating an Vue instance we must configure it with attributes like data or methods etc defining the logic of the vue app. We can later access those attributes by calling vm.<attribute>or this.<attribute> within the instance or component. We can also access the vm object from other instances or normal java script functions.

Both examples below illustrate same app taking input and mirroring it in paragraph and trigging a function with futton that increase a counter and prints it out. To use Vue in an existing site controlling part of HTML through a "vue-widget". This allows you to start using Vue on existing sites, which is why Vue prides itself on being a progressive framework. This is a great option when migrating an existing project using a library like JQuery to Vue.

__________________index.htm______________________
<html lang="en">
   <head>
      <!-- Import Vue 3 package -->
      <script src="https://unpkg.com/vue@next"></script>
   </head>
   <body>
      <h1>Basic Vue example</h1>

      <div id="app">
         <!-- template defined in vue configuration will be added here -->
         <!-- template can also be added here. We choose to demo adding it through javascript as string -->
      </div>

      <!-- link html with vue instance -->
      <script src="./main.js"></script>
   </body>
</html>
__________________main.js______________________;
var app = Vue.createApp({
   // main template of this app
   // note! normally this template is defined in html or in single file component
   template: `
    <div>
      Name:<input v-model='name' />
      <button @click="increaseAge()">Increase age</button>
      <p>Name is {{ name }} and age is {{ currentAge }}</p>
    </div>
  `,
   // parameters to access in the app
   data() {
      return {
         name: null,
         age: 0
      };
   },
   // "functions" executed only when used parameters change value
   computed: {
      currentAge() {
         return this.age;
      }
   },
   // methods that usually handle evens triggers from HTML
   methods: {
      increaseAge() {
         return this.age++;
      }
   }
});

// Connect our app instance with HTML
app.mount('#app');

Same as above but as Single Page Application (SPA) build with Vue-Loader package and webpack build tools. This lets us use advanced features of Vue and take advantage of bundlers. To make building apps with Vue easier use Vue CLI autogenerate configurated project.
__________________index.html______________________

<html lang="en">
   <head></head>
   <body>
      <div id="app"></div>
      <!-- built files will be auto injected -->
   </body>
</html>
___________________main.js________________________;
// Entry point for webpack bundler
import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app');
___________________App.vue________________________
<template>
   <div>
      Name:<input v-model="name" />
      <button @click="increaseAge()">Increase age</button>
      <p>Name is {{ name }} and age is {{ currentAge }}</p>
   </div>
</template>

<script>
   export default {
      // parameters to access in the app
      data() {
         return {
            name: null,
            age: 0
         };
      },
      // "functions" executed only when used parameters change value
      computed: {
         currentAge() {
            return this.age;
         }
      },
      // methods that usually handle evens triggers from HTML
      methods: {
         increaseAge() {
            return this.age++;
         }
      }
   };
</script>


↩ Back To Top

The interpolation {{ }} or v-bind commands you see in Vue controlled HTML code are called a directives. Directives are usually prefixed with v- to indicate that they are special attributes understood and executed by Vue only. Here are some build-in directives that work out of the box but you can create self defined directives too.

{{ <data> }} Text interpolation. Adds property value from vue instance. <p>Name is {{name}}</p>
v-html="<data>" Adds value as HTML between HTML tags. <div v-html="some-html"></div>

v-bind:attribute="<data>"
or
:attribute="<data>"
One way binding. Binds vue instance property to HTML attribute. <img v-bind:src="imageUrl"> or
<img :src="imageUrl">
v-model="<data>" Two way binding. Binds vue script property to HTML field. By default sync data after "input" (for each char) but with lazy use "change". When using with custom components then custom component must implement prop :modelValue and emit @update:modelValue but this can be customized. Read more here. Also in vue 3 we can to other parameters, read more here. <input v-model="name" />
v-model.lazy
v-model.number/trim/
v-on:<event>="fnc()"
or
@<event>="fnc()"
Handle event with correcponding Vue method
Note! Pure @click="handleEvent" (method name without params) will trig method handleEvent(event) where event.target is the calling elem. If params are used then $event must be explicitly defined as input
<button @click ="handleEvent($event, 5)">Add</button>
@<event>.stop Stops (propegation) the bubbling of an event to parent elements as in js elem.stopPropagation().* <button @click.stop="doThis()">Add</button>
@<event>.prevent Prevents the default handling of event as in js elem.preventDefault(). If form case HTTP post not done and refresh of page not performed.* <form @submit.prevent="handleSubmit()"><input type=text" /><input type="submit" /></form>
@<event>.enter.space Trigger event function only for enter & space keys. <input @keyup.enter.space="fnc()" />
v-if="true/false"
or
v-show="true/false"
HTML elem invisible if false in both cases. With 'if' the object is totally removed from DOM and cannot be interacted through DOM. With 'hide' it is just hidden but not element not removed from DOM. in both cases the element do not take up any space (opposite to css property visibility:hidden). Use v-show when toggling visibility often for performance as adding/removing elem from DOM cost.
v-else and v-else-if can be used together with v-if but must follow directly in block after.
But what if we want to toggle more than one element? In this case we can use v-if on a <template> element, which serves as an invisible wrapper.
<span v-if="bVisible">hey</span>
or
<div v-show="bVisible">hey</div>
v-for="(elem, index) in <array>" :key="unique-id"
or
v-for="(value, name) in <object>" :key="unique-id"
Renders elements based on data in Array or Object. Note! Each element must be marked with unique Key value in order for Vue to identify elements correctly when removing. Note! Index in the array is not unique as elem nbr 1 gets index 0 when elem nbr 0 is removed. This must be something unique for each elem that never changes. <ul><li v-for="obj in array :key="text">{{ obj.text }}</li></ul>

*Events can be modified to do different things when cached. Here for example execute vanila js event.stopPropagation() by simply adding stop after event as below in <span @mousemove.stop="">text</span> or vanilla js elem.preventDefault() as <input @submit.prevent="submitForm()"> that prevents the default behavious of sending a HTTP request and refreshing the page. For key-events we can bind certain keys to react for example execute alert function only when enter key was pressed <input @keyup.enter="submitForm()">


↩ Back To Top

▸ data
Declaration of properties to use within an vue instance. No calculations possible here. Can be reached from this from outside in vue instace but not in data.

data() {
 return {
   name: 'Martin',
   age: 2
   callback: function() { ...}
   person: {name: 'anna', id: 1}
   // name2: this.name   DO NOT WORK
 }
}

↩ Back To Top

▸ props
Used to send data to a child component from parent. Provided data can be a primitive type (String, Number, Boolean, Date, Symbol) or a reference types (Array, Object, Function). All data sent to children as props IS REACTIVE, meaning if data change in parent it is reflected in the child. Data can be mutaded by child only for a reference type (i.e. object) as then child have a pointer to the memory (unlike to primitive type where child has only a copy of the data and changes in child are not refleced in parent).

You can pass a function as a prop but this is not a recommended pattern of comunication from child to parent in Vue (unlike React where children communincate with parent is thought sent functions). Instead in Vue child can emit an event, and this events in turn when received at parent can trigger a function that modifies the data.

______________________ parent _________________________________

<my-friend name="Alex" age="30" :person="personObject" />

// where

personObject: {
  name: 'Anna',
  age: 16
}
_____________________child (MyFriend.vue)____________________
<template>
  <p>Person is {{ name }} and is {{ age }} years old.</p>
  <p>Same person is {{ person.name }} and is {{ person.age }} years old.</p>
</template>

export default {
  ...

  // short definition of prop this component receives
  props: ['name', 'age', 'person']

  or

  // more descriptive way of what props this component receives
  props: {
    name: {
      type: String,
      default: 'not set'
    },
    age: {
      type: Number,
      required: true,
      // we can also validate the received value
      validator(value) {
        return value<0 ? false : true;
      },
    },
    person: {
      type: Object,
      default() {
        return {
          name: 'not set',
          age: ''
        }
      }
    }
  }

  ...
}

↩ Back To Top

▸ emits
Used to send custom events with or without data from a child component to parent.
If you emit an event from the child named in camelCase, you will be able to use in listener in parent as kebab-cased. It is recommended to define all emitted events with emits: {...} in order to better document the component api aswell as not forgetting to implement some emmits (otherwise build error).


Can custom emit event pass more than one value?
Yes. But as custom events in Vue by design only accept two parameters (name and "eventData") then you must encapsulate your values in an object. Note that our "eventData" from child in parent inline listener can be referenced as $event. If we redirect the event directly to a function with paramaaters then the value object will be splitted in seeral parameters.

Example :

______________________ parent _________________________
// different ways of receivin same emmited custom events
<component-x  @doSomething="onDoSomething"  @click="onClick"  @modify-data="onModifyData_1" />
<component-x  @doSomething="onDoSomething"  @click="onClick"  @modify-data="onModifyData_2($event)" />
<component-x  @doSomtehing="onDoSomething"  @click="onClick"  @modify-data="onModifyData_3($event.action, $event.id)" />

// where

methods: {
  onModifyData_1(event) {
    // event.id is '1'
    // event.action is 'delete'
    ...
  },
  onModifyData_2(event) {
    // event.id is '1'
    // event.action is 'delete'
    ...
  },
  onModifyData_3(id, action) {
    // id is '1'
    // action is 'delete'
    ...
  },
  onDoSomething() {
    ...
  },
  onClick(event, id) {
    // do something with element with is
    ...
  }
}
___________________child (ComponentX.vue)_______________________
<template>
  // only onModifyData_x() method in parent is called (.stop modifier stops the native click-event)
  <button @click.stop="$emit('modifyData', {action: 'delete', id: 1})">Send custom event</button>

  // both onDoSoething() and onClick() methods in parent are called (becouse components tunnel native events)
  <button @click="$emit('do-something')">One more custom event</button>

  // only onClick() method is called
  <button>Send native event</button>
</template>


<script>
export default {
  // Short definition of events this child sends
  emits: ['modifyData', 'do-something']

  or

  // more descriptive way of event this child sends
  emits: {
  modifyData: function(action, id) {
    if (action != null && id != null)
    {
      return true
    }
  },
  'do-something': null,   // No validation
  }

  // Note! We do not define click as it is native event alwats tunneld
}
</script>

↩ Back To Top

▸ provide/inject

Used to pass data to from parent to any childred components. Provided data can be a primitive type (String, Number, Boolean, Date, Symbol) or a reference types (Array, Object, Function). Provided primitive data is NOT REACTIVE, meaning if data change in parent it is not reflected in child. Data can be mutaded by child only for a reference type as then child have a a pointer to the memory (opposite to primitive type where child has only a copy of the data). But is is NOT RECOMENDED as it is hard to follow in larger code base and difficult to debug. Insted send a function that child can call that do modifications on the data. Only component holding the data shall modify the data!

Example:

export default {
  ________________________ parent ____________________________
...

data() {
  return {
    resources: [
        { name:'Anna', id: 1 },
        { name:'Sara', id: 2 },
    ],
  }
},
methods: {
    deleteResourceMethod(id) {
      // This works in parent but overwites pointer sent to children = no reaction in children!
      // this.resources = this.resources.filter((res) => res.id != id)
      // Use splice instead as it do not modify pointer on resources:
      const i = this.resources.findIndex((res) => res.id === id)
      this.resources.splice(i, 1)
    }
}
provide() {
    return {
        // Any children can inject and use resources.
        providedResources: this.resources,
        // Any children can inject and call this to delete resources
        providedDeleteResourceMethod: this.deleteResourceMethod,
    }
}

...
}
____________________________ child x ______________________________
<template>
   <ul>
      <li v-for="res in providedResources" :key="res.id">
         <button @click="providedDeleteResourceMethod(res.id)">Remove {{ res.name }}</button>
      </li>
   </ul>
</template>

export default { ... inject: ["providedResources", "providedDeleteResourceMethod"], ... }

↩ Back To Top

▸ computed
Functions that are recalculated (executed) at initialization and then only when variables they depend on change value.

computed: {
  getFullName() {
    return this.fname + ' ' + this.lname
  }
}

where

data() {
  return {
    fname: 'anna'
    lname: 'woo'
  }
}

↩ Back To Top

▸ methods
Functions that can be called for execution. Note! Mostly used to handle events. If accessed from HTML through interpolation or in v-bind then Vue executes the function every time SOMETHING changes in DOM as Vue do not know what a method might depend on. It is then bad for the performance and computed should be used instead then. If params are used then $event must be explicitly defined as input

// Might be called like this
<a-component @click="increaseAge($event, 16 )">

then

methods: {
  increaseAge(event, years) {
    console.log('Called by: ' + event.target)
    this.age += years
  }
}

// Might be called like this
<a-component @click="increaseAge">

then

methods: {
  increaseAge(event) {
    // if even is native event then we can access target like i vanilla javascript
    console.log('Called by: ' + event.target)
  }
}

// Note! event can be customized in child that send them and then usualy an object with data contaning parameters.

↩ Back To Top

▸ watch
Listener for changes in corresponding properties. Executed only if corresponding data property with same name changes. Used to proxy changes in property, meaning add some extra functionality when a change accures. Also used to bind class objects to dynamically bind classes.

watch: {
  name: function (newValue, oldValue) {
    console.log("The name changed: New is: %s, old was: %s", newValue, oldValue)
  }
}

where

data() {
  return {
    name: 'anna'
  }
}

↩ Back To Top

▸ Life-cycle hooks
Life-cycle hooks are callback function executed at diffrent "states" of an instance or component. Read more here about understanding Vue Lifecycle hooks

created() {
  // called when general app configuration initialized i.e. data and methods. Nothing shown on the screen and the template is not compiled (nor verified) yet. $refs can not be accessed here.
  ...
},
mounted() {
  // called when template was parsed, all interpolation values exchanged to the values to be shown to user and DOM is drawn to the screen (beforeMounted() is same but just before it is drawn on the screen). $refs can  be accessed here.
  ...
},
updated() {
  // called when dat change trigged an update of the DOM and the changes were processed. The updated change is visible on the screen. (beforeUpdated() is just before it is visible on the screen)
  ...
}

beforeUnmount() {
  // called just before the app is unmounted() and dead. Good place to execute any clean up code such or sending HTTP request informing about shutdown. Can we access $refs here?
  ...
}

↩ Back To Top

Central local database to read/write data to. Never acces state directly from components (only from getters/mutations). Declare Store like this:

import { createStore } from 'vuex';
import InstructorsModule from './InstructorsModule.js';

export default createStore({
    state: {
      uid: null,
      isAuthorized: false
    },
    mutations: {},
    getters: {},
    actions: {},
    modules: {
        instructors: InstructorsModule,
    }
})

// while module declared like this with seperate files for actions/getters/mutations

import mutations from './mutations.js';
import actions from './actions.js';
import getters from './getters.js';

export default {
    namespaced: true, // name: instructors
    state() {
        return {
            instructors: []
        };
    },
    mutations,
    actions,
    getters
};

Actions
Perform async actions like HTTP Requests and calling other getters/mutations/actions

export default {

    async actionA({dispatch, commit, getters}, payload) {

        // Call mutation from action
        commit('[mutation-name]', payload);

        // Call getter from action
        let result = getters.[getter-name](payload);

        // Call action from action
        let result = await dispatch('[async-action-name]', payload);

        return 'success'
    }

    // Call action from component <template>
    $store.dispatch('[module-name]/[action-name]', {data});

    // Call action from component <script>
    this.$store.dispatch('[module-name]/[action-name]', {this.data});
}

Getters
Call state to receive data or call other getters.

export default {
    getter_getOne => (state) => (id) {
        // Return one
        return state.users.find((elem) => elem.uid == id);
    }

    getter_getAll(state) {
        // Return one
        return state.users
    }

    getter_getFiltered => (state, getters) => (filters) {
        // Return filterd only or all if no filter
        if (filters) {
            return state.users.filter((elem) => elem.loacation == filters.loacation);
        } else {
            // Call a getter from getter
            return getters.getAll;

        }
    }

    getterA: (state, getters) => (payload) => {
        // Call state from getter - filter on location
        return state.users.filter((elem) => user.loacation == payload.loacation);


    }

    // Call getter from component <template>
    $store.getters.[getter-name]({data})

    // Call getter from component <script>
    this.$store.getters.[getter-name]({this.data})
}

Mutation
Call state to modify data and call other mutations.

export default {
    mutation_setA: (state, payload) {
        // Call state from mutation. Replace (set) A
        state.A = payload.A;
    }

    mutation_addA: (state, payload) {
        // Call state from mutation. Append (add) A
        state.A = [...state.A, ...payload.A];
    }

    // Call getter from component <template>
     $store.commit('[mutation-name]', data)

    // Call mutation from component <script>
    this.$store.commit('[mutation-name]', this.data)
}

↩ Back To Top

.
├── app.css
├── App.vue
├── assets
│   └── ...
├── components
│   └── ...
├── main.js
├── mixins
│   └── ...
├── router
│   └── index.js
├── store
│   ├── index.js
│   ├── modules
│   │   └── ...
│   └── mutation-types.js
├── translations
│   └── index.js
├── utils
│   └── ...
└── views
    └── ...
  • public - Any assets placed here will simply be copied in production build and not go through webpack. You need to reference them using absolute paths.Note! Those are not minified and bundled together. as an escape hatch, and when you reference it via absolute path, you need to take into account where your app will be deployed. Red more here.
  • assets — Where you put any assets that are imported into your components
  • components — All the components of the projects that are not pages
  • mixins — Mixins are javascript code that is reused in different components. In a mixin you can put any component’s methods from Vue.js they will be merged with the ones of the component that uses it.
  • router — All the routes of your projects (i.e. in index.js). Basically in Vue.js everything is a component. But not everything is a page. A page has a route like “/dashboard”, “/settings” or “/search”. If a component has a route it is routed.
  • store — Store modules contaning global data. Vuex modules in the subfolder modules which are then loaded in the index.js.
  • translations — Locales files i.e. Vue-i18n, and it works pretty good.
  • utils — Functions to use in some components, such as regex value testing, constants or filters. - pages — Components that are routed. They represent pages and they have routes. This files only comunicate with the store!

Smart components are also called containers, they are the ones who handle the state changes, they are responsible for how things work. On the opposite, the dumb components, also called presentational, only handle the look and feel.

To differentiate dumb components that are use in many places in UI we store those in a folder named ui and all the names start with Base i.e. BaseButton, BaseFrame, BaseDivider. Those are constructed to be generic and suid the overal site UI and can sometimes be customazible by calling component.

Smart components are grouped by feature they belong to and might have own folder in components for those belonging together. We always start to use feature as first part of name i.e. ProductsList, ProductsFilters, ProductCard, ProductsCalltoAction.

Only pages can be single worded as they are called by router and will never conclict with naming of default html elements that always are single-orded.

Components that are only used once per page should begin with the prefix “The”, to denote that there can be only one. For example TheNavbar.vue TheHeader, TheFooter, TheFooter.vue, ThePageWrapper.

Child components should include their parent name as a prefix. For example if you would like a “Photo” component used in the “UserCard” you will name it “UserCardPhoto”.

MVC pattern:
dumb components -> View
smart components -> Controller
store -> Model

↩ Back To Top

Properties in this within vue instance starting with $ are the native vue objects we can access.

$data: the data object in constructor to reach properties with this.$data.<property-name>

$refs: Easy way to mark and later easily reach any HTML element save in DOM. Note! Using this is not recommended for setting new DOM elements as Vue template when rendered will overwrite those. But recommented use is to reach certain element with this.$refs.<name>

Example of $refs:

// HTML. First ser a ref on a html attribute with a name
<button ref="coolButton">Press me</button>
<input name="number" ref="numberInInput" value="10123"/>

// Vue
let text = this.$refs.coolButton.InnerText // Press me
let  input = this.$refs.numberInInput.Value  // 10123

// This is widly used on input element to get whole entered word when submitted insted
// of listinging character by character and saving to data

↩ Back To Top

Events can be sent from chil to parent and be native or custom. Native events are sent by default by various HTML eleemts. I.e Button sends click event and input submit when a charactes changes within it. Those events are tunneled to parent from chhild componets always by default. Thos do not need to be emmited.

Custom events are those that we emit. See emits section for that here.

Event carry always $event data. For native events it is an object descripting eleemnt that emmited the event $event.target. For custom events it might be one single value or object with data depending on design.

Modifiers

To prevent default behaviour of an native event vue can modify it when catched.

<component-x  @click.stop="doSomething" />

click.stop: Prevents native event from beeing tunnel to further components.
click.prevent: Prevents default action i.e the submit on form will no longer http post and reload the page

Key events like keyup can also be modified.

For example:
@keypp.enter="dosomething()" only triggers for enter key.

Read mmore here

↩ Back To Top

Components together with main vue instance are the main building blocks of Vue applications. There are several ways to create a components. Usually parameters like data, methods, computed and props are defined on a component itself and valid only locally within the component.

A template can be an textual attribute like template: '<p>Hello world</p>' added manually to vue instance (Vue.template(...)) but far most useful and powerful method of defining templates is to use <template> tag in single-file components in separate .vue files. For this to work the projest must be build as a vue-loader plugin must be executed by a bundler like Webpack. vue-loader parses the .vue file. For this to work our files must be compiled/bundled with webpack and vue-loader. Vue project generated with Vue CLI contain all necessary plugins by default to di this automagically.

Naming of a component is important and can be as kebab-case my-component-name or PascalCase MyComponentName but when defining a component with pascalcase like MyComponentName it shall be referred in html as my-computed-name. Vue files shall be PascalCase MyComponentName and contain of 2 words. Components that are used only once in the app usually are named TheHeader or TheFooter.

Below is an example of parent using child component and child and sends some data to it. Child component on other hand is emmiting an event to parent when a button is pressed within the child.

We can have local and global components. Local are defined with onother component and can onl be accesed by it and it children. Global are registers in main.js directly on Vue instance and can be accessed by all components. UI companents like BaseButton or BaseCard are usualy global as they are used on many places.

_________________ComponentY.vue___________________
<template>
   <div>
      <button @click="emmitThis()">Togge visibilty</button>
      <p v-show="isEnabled">My name is {{ name }}</p>
   </div>
</template>

<script>
   export default {
     ...
     props: {
       // reactive properties sent from parent
       name: String
       id: String,
       isEnabled: Boolean
     }
     ...
     emmitThis() {
       // this send event with data to parent that might change visibilty on isEnabled
       // then this text in child will not be visible.
       this.$emit('toggle-visibility', this.id)
     }

   }
</script>
__________________main.js______________________;
// If developing with bundlers like Webpack & vue-loader
// templated single-file component can be imported
// from a separate .vue file into a .js file.

import componentY from './ComponentY.vue';
Vue.component('component-y', componentY);
_________________index.html (or any other element)___________________
...
<component-y
  :isEnabled="isVisible"
  name="Martin"
  id="1"
  @toggle-visibility="doShow = !doShow"
/>

where

data() {
  return {
    doShow: true
  }
}
...
</script>

↩ Back To Top

Slots help you distribute code in other components. Good to build reusable widgets like a slideshow. A template can contain a named slot that can be dynamically modified from outside. Default content in single-file template slot can be added and it is shown until the slot is not used/filled with content.

Note that parent is rendering both style and text before from outside the template (style rendering from the child itself is possible too)

CHILD - "my-template"
<template>
	<div>
		<h1 name="title">Title</ha1>
	</div>
	<div>
		<p class="my-subtitle" name="subtitle">Subtitle</p>
	</div>
</template>

PARENT -populating the slots
<my-template>
	<span slot="title">My Tilte</span>
	<span class=".my-content" slot="subtilte">{{ text }}</span>
</my-template>

<script>
export default {
    data() {
        return {
            text: 'My Subtitle'
        }
    },
};
</script>

<style scoped>
	.my-subtitle {
		color: darkcyan;
		font-size: larger;
	}
</style>

↩ Back To Top

Binding Class or Style
There are two ways to bind the HTML class attribute to Vue data.

  • As an array of strings with names of the classes: ['class-name1', 'classname2']
  • As an object with boolean value defining if class is active or not: {'class-name1': true, classname2: false}
<!-- Class bound to object with key/value pairs -->
<!-- Key is the class name (if two-worded then within '') and value either true/false -->
<!-- deciding if this class shall be enabled or not.  -->
<div class="btn" :class="{'btn-wide': isWide, active: isActive}"></div>

<!-- Same as above -->
<div :class="{btn: true, 'btn-wide': isWide, active: isActive}"></div>

<!-- Same as above -->
<div :class="classObject"></div>
<!-- where -->
computed: { classObject() { return { btn: true 'btn-wide': this.isWide, active: this.isActive } } }

<!-- Class bound as an array of class names defined as strings -->
<div class="btn" :class="['btn-wide', 'active']"></div>

<!-- Finally as a mix -->
<div :class="['btn', {'btn-wide': isWide}, activeObject]"></div>

Binding to style is also possible but normally not recommended as inline styling shall be avoided in modern web development.

<!-- Style bound to object with key/value pairs. -->
<!-- Important! For properties with two-words use camelCase or "" around. Values must be strings! -->
<p :style="{color: 'black', backgroundColor: definedColor, 'font-size': sizeOfFont  + 'px'}">text</p>

<!-- Style bound to array with objects defined as computed properties -->
<!-- Null disables the styles -->
<div :style="[baseStyles, overridingStyles]"></div>
<!-- where -->
computed: { baseStyles() { return null; }, overridingStyles() { return { backgroundColor: 'red', 'font-size': '20px' } } }


↩Back To Top

In main entry script in Vue 3 app (usually main.js or app.js) we ofte do create our app, router, store, global components, global styles etc. Here we can also set global properties to be reached in all .vue files in the project. A global propery provided this way might be marked with $ to be easialy distinguished in code for better readability. Example:

------------------- main.js -------------------------------
// Set global properties on app object
...
import { something } from './some-file.js';
app.config.globalProperties.$something = something;


------------------ any-component.vue ----------------------
// Our something can now be accessed in 3 diffrent places in .vue files
// In template (html), options api (js) and composition api (js).
<template>
    <p>This is {{ $something }}</p>
    <p>This is {{ getSomething }} too</p>
</template>

<sript>
import { getCurrentInstance } from 'vue';
export default {
  computed: {
        getSomething() {
            return this.$something
        }
  },
  setup(props, context) {
    ...
    let $something = getCurrentInstance().appContext.config.globalProperties.$something;
    console.log('something = ' +  $something)
  }
</sript>



--------------------  any-js-file.js --------------------------
// To reach the global propery in .js file (like actions.js) you must import it like in main
import { something as $something } from './some-file.js';
...
console.log('something = ' + $something)

Note: Evan You (Vue creator) says: "config.globalProperties are meant as an escape hatch for replicating the behavior of Vue.prototype in Vue 2. In setup functions (composition API), simply import what you need or explicitly use provide/inject to expose properties to app."


↩Back To Top

Composition API come in Vui 3 and was specialy designed to be customizable in a way how code can be structured and to make it possible to reuse code.

setup() is only called once and it is before monted() therfore this is not possible to be used in it as we do in options api.

props is an object always passed as first parameters to setup where each prop passed to the component is a property.

context is a normal an object with three component properties:
attrs – Attributes (Non-Reactive) ??? TODO
slots – Slots (Non-Reactive) ??? TODO
emit – Method (Emit events) ??? TODO

<template>
  <p> {{ combinedName }}</p>
<template>


<script>
import {ref, toRef} from 'Vue'
import CompA from './CompA'

export default {
  props: {
      name: String,
  },
  components: {
      CompA
  },
  setup(props, context) {
    let age = ref(33)

    let name = props.name; // Wrong. Will lose reactivity.
    let name = toRef(props, "name"); // Correct
    console.log(name.value);

    let combinedValue = `${name.value} ${age.value}`

    // Logics here

    // Anything returned will be available outside
    return {
      combinedValue
    };
  },
};

</script>
setup(props, {attrs, slots, emit}) {
...

// in composition api we can watch several variables at once.
watch([category, location], ([newCat, newLoc]) => {

TODO Not finished

    let C = newCat;
    let L = newLoc;

}

return {
    category,
    location
  }
}


↩ Back To Top

Webpack and bable transform our javascript code. An web app witteb with javascript framework as Vue can contain a lot of javascript needed to be downloaded to the client at initial HTTP request or subseqient request (lazy loading). Webpack by default minimilizes our code when buildng for production. We can write javascrit in a certain way to help webpack to minimalize amount of javascript code sent to client.
Look at the example below:

delete(state, id) {
    DEBUG ? console.log(`${module}/delete()`) : '';

    const index = state.resources.findIndex((elem) => elem[idKey] == id);
    if (index >= 0) {
        // found -> delete
        state.resources.splice(index, 1);
        state.loadStatus.current = state.resources.length;
    } else if (DEBUG) {
        // this should never happen
        console.error('Error. Resource to delete not found.');
        throw 'Error. Resource to delete not found';
    }
}

357 characters
transforms to
293 characters

delete: function (e, t) {
b && console.log(''.concat(h, '/delete()'));
var n = e.resources.findIndex(function (e) {
    return e[p] == t;
});
if (n >= 0) e.resources.splice(n, 1), (e.loadStatus.current = e.resources.length);
else if (b)
    throw (
      (console.error('Error. Resource to delete not found.'),
      'Error. Resource to delete not found')
  );
}

or

delete(state, id) {
    DEBUG ? console.log(`${module}/delete()`) : '';

    let resources = state.resources;
    let loadStatus = state.loadStatus;

    const index = resources.findIndex((elem) => elem[idKey] == id);
    if (index >= 0) {
        // found -> delete
        resources.splice(index, 1);
        loadStatus.current = resources.length;
    } else if (DEBUG) {
        // this should never happen
        console.error('Error. Resource to delete not found.');
        throw 'Error. Resource to delete not found';
    }
}

393 charactes
trasforms to
281 characters

delete: function (e, t) {
  b && console.log(''.concat(h, '/delete()'));
  var n = e.resources,
      r = e.loadStatus,
      o = n.findIndex(function (e) {
          return e[p] == t;
      });
  if (o >= 0) n.splice(o, 1), (r.current = n.length);
  else if (b)
      throw (
          (console.error('Error. Resource to delete not found.'),
          'Error. Resource to delete not found')
      );
}

Additionalyy we can instruct babel/webpack to remove console.log in production with

then ony 240 characters

delete: function (e, t) {
    var n = e.resources,
        r = e.loadStatus,
        a = n.findIndex(function (e) {
            return e[h] == t;
        });
    if (a >= 0) n.splice(a, 1), (r.current = n.length);
    else if (f)
        throw (
            (console.error('Error. Resource to delete not found.'),
            'Error. Resource to delete not found')
        );
}

It can be seen that changes increasing size of code in dev decreased size of code after webpack minimalization for in production.
It is good to understand and write code in a way that it is easy to follw/read for developers and as minimilazed as possible in production.

  • Use logger plugin (like winston) or remove unwanted console.logs in production (transform-remove-console extension)
  • Use short minimalistic texts in your logs.
  • Use as short function names as possible without loosing readability.
  • Use short properties names.
  • Fore repeftive access to object/array properties with long names define those as variables in beggining of functions instead and use those. Minimizer will then change those local names but it can not change names of properties.


↩ Back To Top

  • Create a ErrorService class in a js file
  • Use swal
  • Create a Vues State action that get


↩ Back To Top

EsLint detects syntax errors and have diffrent rules and warns/error when not followed. ESLint is not only able to detect errors in your code, but in many cases, can even automatically correct them for you.

Followin can be used for Vue 3 project:

'eslint:recommended',
'plugin:vue/base',
'plugin:vue/vue3-essential',
'plugin:vue vue3-strongly-recommended',
'plugin:vue vue3-recommended'

Eslint plugin auto fix (format) some fixable proposed warings/errors by running npm script lint with --fix option or set followin in vsc for save actions:

"editor.codeActionsOnSave": { "source.fixAll.eslint": true }

Installing eslink-plugin-vue to get access to these rules that are defined in .eslintrc.js.

While eslint plugin warns and gives you error whn you are building if you want live warning and visual aid (like red line on error in editor without building, whilte saving) then you need to install and enable eslint extension for vscode to enable vscode integration for eslint.

Prettier is a populat formatter. You install it as an extension to vsc and there are very few (almost none) setting. It just runs and formats out of the box. It can format automatically on save when set in setting: "editor.formatOnSave": true

Important!
You must turn off ESLint's formatting rules that would conflict with Prettier. Without this those two will always mismatch and fight in formatting you code and warning in a never ending mess.

Install: npm install eslint-config-prettier --save-dev

Then add LAST in .eslintrc.js file:

extends: [ 'eslint:recommended', ... "prettier" ],


↩ Back To Top


↩Back To Top

It is a UI Framework specialy designed for Vue utilizing slots and props and is very customizable.

Navigation bar

<v-app-bar>
   // Navigation row

   <v-app-bar-nav-icon /> // styled icon "hamburger" button

   <v-toolbar-items><img /></v-toolbar-items> // Branding logo

   <v-toolbar-title /> // Branding text

   <v-spacer /> // space to put navigation item to right

   <v-navigation-drawer /> // Side navigation. Small screen. (trigged by hamburger)

   <v-toolbar-items /> // Top navigation. Big screen. When side nav is hidden.

   <v-container /> // Log in box
</v-app-bar>

Some logic must be applied on all this, like hiding hamburger/side and top navigations. See details in vuetify documentation.


↩Back To Top

As Bootstrap 5 do not use jQuery then Bootstrap 5 can be directly included in project.

But bootstrap-vue add extra functionality as it wraps the elemants in vue components and it easier to distinguish parts of code. Still it is totally ok to use Boostrap 5 directly in vue.

Navigation bar

<!-- Navigation flex row with items -->
<nav class="navbar">
   <!-- Brand Icon & Text -->
   <!-- First flex item: Max to left -->
   <a class="navbar-brand"><img />Branding Text</a>

   <!-- Toggler hamburger button -->
   <!-- Second flex item: Max to right  -->
   <button class="navbar-toggler" />

   <!-- Top/Mobile navigation (controlled by toggler) -->
   <!-- This gets "flex-basis: 100%" - why it's always on new row!!! -->
   <ul class="navbar-nav">
      <li class="nav-item" />
      <li class="nav-item" />
   </ul>
</nav>


↩Back To Top


Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS). Nuxt's goal is to make web development powerful and performant with a great developer experience in mind.

What is convenient with nuxt is that it sets up routing and server side rendering ans static site generation out-of-the-box. Most of this can be done in vue without nuxt but nuxt package and highly optimizes the files and save us a lot of overhead work.


↩ Back To Top

Nuxt can scaffold a project with a predefined project file structure. Only requirement is to have node.js installed on your device. You scroffor a new project in new folder with a simple command. Read more nuxtjs.org/guide

> npx create-nuxt-app [project-name]

Installation process described nuxtjs.org/guide/installation

options:

- choose name, description and author
- choose JavaScript or TypeScript (your prefered language) (default JavaScript)
- choose package manager (default Npm)
- choose UI framework module such as Bootstrap Vue, Bulma etc. (default none, recommended Bootstrap Vue)
- choose Server framework module such as Express, Koa etc (default none, recommended Express)
- choose additional nuxt modules such as detenv, axios etc (default none)
- choos linting tools such as ESLint, Prettier etc (default none, recommended ESLint)
- choose test framework jest, AVA (default none)
- choose application type universal (SSR - Server Side Rendered) or Single Page App (SPA) (default SSR)
- choose development tools. jsconfig.json is recommended and default.

To undestand the created folder structure read **here**.

Note! You can always at later stage change the configuration of you project.

  • Project project name, description and author. In package.json file.
  • Preferred language. In ???
  • Package manager. In ???
  • UI framework. In package.json file. Add/remove devDependencies
  • Application type. In nuxt.config.js file. Set mode to "universal","???" or "???".


↩ Back To Top

@TODO


↩ Back To Top

Attributes

standard vue attributes...

Components:
Components can be imported in pages and layouts. Insted of importin header to each page is common to import i once to layout!

import Header from './data/Header.vue';
import Header from '~components/data/Header.vue'; // ~/@ refers to the root folder. Good for copy/paste
import Header from '@components/data/Header.vue'; // ~/@ refers to the root folder. Good for copy/paste

Pages:
This folder contains routes meaning pages that users can navigate to. The main default entry point is:

index.vue

Then folder structure decides what possible routes are allowed. Pages are as normal components but with some extra functionality on topadded by nuxt. For example: validation.

Layouts
A main wraping element on the page. All pages and components can be wrapped in a ind of "frame" layout. A layout contain a tag <nuxt></nuxt> and this is the main "layout-frame" where all elements are loaded. The style in a layout defined in its <style></style> is unusually the main default style for the whole application (similar to css: [] in nuxt.config.js).

We can add several the layouts in the layouts folder. Additional layouts (except default.vue) must have extension .vue - [name].vue. Then in each page that shall be loaded into this layout uses sa specialattribute in its script-data:

<script>
export default {
...
layout: '[name]',
...

Error layout
It is a type of default layouot but with the reserved name error.vue. If exist it will by default be used for all errors.

Plugins
Here you can add any global .js programms that are run before the app is mounted. This make it possible to insert functionality that can be used globally such ass global components, filters, event-busses, variables etc

Following code demonstates how we can save time not needing to import components that are used almost everywhere. Create global component in a plugin that is run before main app instance is created. Then we can use the component in file templates withou importing and declarin those in out <script> parts.

core-components.js
------------------

import Vue from "vue"

import AppButton from "@components/UI/AppButton"
import AppControlInput from "components/UI/AppControlInput"

Vue.component('AppButton', AppButton)
Vue.component('AppControlInput', AppControlInput)
nuxt.config.js
--------------

...
plugins: [
  '~plugins/core-components.js' // must be added here
],
...

Modules
Here you can add utility functions (Nuxt.js extensions extending the framework's core functionality) to our app that have been created by others. Nuxt can be extended with configuration options and plugins, but maintaining these customizations across multiple projects is tedious, repetitive and time-consuming. This is one of the reasons why Nuxt provides a higher-order module system that makes it easy to extend the core. A list of modules can be found here. If you want to read more deeply about what a module is check here.

Adding a module is simple.

First instal npm package:
-------------------------

npm install -D bootstrap-vue
npm install -D @nuxtjs/axios
nuxt.config.js
--------------

...
modules: [
  // Doc: https://bootstrap-vue.js.org
  'bootstrap-vue/nuxt',

  // Doc: https://axios.nuxtjs.org/
  '@nuxtjs/axios',
],
...

Now you can use the extended bootsrtap and axios functionality globally in the app as descripbed in Deocumentation.

Validation
validate shall be place in script-data part of page that receives the route and is called every time before navigation to a new route. It will be called server-side once (on the first request to the Nuxt app) and client-side when navigating to further routes. This method takes the context object as an argument.

export default {
  ...
  validate ({ params, query, store }) {
    // Must be a number
    return /^\d+$/.test(params.id)
  }

  // or

  export default {
    ...
    validate (data) {
      // Must be a number
      return /^\d+$/.test(data.params.id)
    }
  }

Nested pages
Normally loaded pages takes up whole new screen. We can add a child "frame" where the output from the route is displayed. It is created by putting a .vue file in same directory as folder name similarly. Then routes from this "outer" vue file will be displayed in the index.vue frame defined inside the folder.

Styling
Typically styling is scoped in pages, it only applies to elements in the page. Same applies to components. They usually contain functional styling. Application wide style is normally added in assest/styles folder and included in nuxt.config.js file. But each layout can contain non-scoped style appling to the layout as well. Therefore for app with multiple layout the method of including global main style from config file is recommended.


↩ Back To Top

Folder structure with index.vue is recommended. If folder name starts with _[name] then [name] is parameter name passed to child in:

$route.params.[name]

Example:

Folder structure
----------------

  /root
  - index.vue     <- main entry point
  - articles.vue  <- parent
  /articles
    - index.vue   <-child (nested) frame
    /_aid
    - index.vue
/root/articles.vue -------------------
<template>
   <div>
      Enter article id:<input v-model="id" /> <button @click="$router.push('/articles/' + id)">Load id {{ id }}</button><br />
      <nuxt-link to="/articles/1">Link to is 1</nuxt-link><br />
      <nuxt-link to="/articles/2">Link to id 2</nuxt-link>

      <!-- Shoe routes in frame. Note style aplied from parent. -->
      <nuxt-child style="margin:50px; width: 300px; height: 150px; background-color: green;" />
   </div>
</template>

<script>
   export default {
      data() {
         return {
            id: null
         };
      }
   };
</script>
/root/articles/index.vue -----------------------

<template>
   <div>
      <p>no article chosen</p>
   </div>
</template>
articles/_aid/index.vue -----------------------

<template>
   <div>
      <p>A single article with id {{ $route.params.aid }}</p>
   </div>
</template>

<script>
   export default {
      validate(data) {
         const errorMsg = isNaN(data.params.aid) ? 'Invalid input. Not a number' : null;
         // is not a number provided then false returned and page is "not found"
         return errorMsg == null;
      }
   };
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment