Getting Started to Vue 3
Published: 2022-04-20 04:54:51

**Install NodeJS**
https://nodejs.org/en/download/

**Install Git**
https://git-scm.com/downloads

**Install Vue CLI**
`npm install -g @vue/cli`

**Install Vuex**
`npm install vuex --save`

**Install Vue Router**
`npm install vue-router --save`

**Create a Project**
`vue create vue-workshop`

Dialog Option Selection:

- Default (Vue 2)
- Default (Vue 3)

> Note: Use Vue 2 if you plan to use Vuex and/or Vue Router



**Run Project**
`cd vue-workshop`

Edit package.json (line 6)
`vue-cli-service serve --host localhost`

`npm run serve`

## VueJS Basics

## Reactivity


`Reactivity.vue`

```javascript
<template>
  <div class="pad-top">
    The time is: {{ date }}
  </div>
</template>

<script>
export default {
  name: "Reactivity",
  data() {
    return {
      date: null,
    }
  },
  mounted() {
    setInterval(() => {
      this.date = (new Date()).toLocaleString()
    }, 1000);
  },
}
</script>

<style scoped>
</style>
```

## Input Binding
`InputBinding.vue`
```javascript
<template>
  <div class="pad-top">
    <span>Type your name:</span>
    <input
       class="margin-right"
       v-model="name"
    />
  </div>
  <div class="pad-top">
    Your name is {{ name }}!
  </div>
</template>

<script>
export default {
  name: "InputBinding",
  data() {
    return {
      name: ‘’,
    }
  },
}
</script>

<style scoped>
.pad-top {
  padding-top: 15px;
}
</style>
```

## Element Visibility based on Input Binding

`InputBindingVisibility.vue`

```javascript
<template>
  <div class="pad-top">
    <span>Type your name:</span>
    <input
       class="margin-right"
       v-model="name"
    />
  </div>
  <div class="pad-top" v-show="name !== ''">
    Your name is {{ name }}!
  </div>
</template>

<script>
export default {
  name: "InputBindingVisibility",
  data() {
    return {
      name: ‘’,
    }
  },
}
</script>

<style scoped>
.pad-top {
  padding-top: 15px;
}
</style>
```

## Event Handling


`EventHandling.vue`

```javascript
<template>
  <div>
    <button @click="toggleMe">Click me!</button>
  </div>
</template>

<script>
export default {
  name: "EventHandling",
  methods: {
    toggleMe() {
      alert(‘I was clicked!’);
    }
  }
}
</script>

<style scoped>
</style>

```


## Conditional Rendering

`ConditionalRendering.vue`

```javascript
<template>
  <div class="pad-top">
    <button @click="toggleMe">Click me!</button>
  </div>
  <div class="pad-top" v-if="toggleDiv">
    HOLA!
  </div>
</template>

<script>
export default {
  name: "ConditionalRendering",
  data() {
    return {
      toggleDiv: false,
    }
  },
  methods: {
    toggleMe() {
      this.toggleDiv = !this.toggleDiv
    }
  }
}
</script>

<style scoped>
.pad-top {
  padding-top: 15px;
}
</style>



```

## List Rendering

`ListRendering.vue`

```javascript
<template>
  <div>
    <div>
      Things to do:
    </div>
    <div>
      <ul>
        <li v-for="listItem in sampleList" :key="listItem.text">
          {{ listItem.text }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  name: "ListRendering",
  data() {
    return {
      sampleList: [
        { text: 'Eat' },
        { text: 'Sleep' },
        { text: 'Be awesome' }
      ]
    }
  },
}
</script>

<style scoped>
</style>

```

## Vue Component Lifecycle
[Link](https://v3.vuejs.org/guide/instance.html#lifecycle-diagram)

## Vue Lifecycle Hooks
[Link](https://v3.vuejs.org/api/options-lifecycle-hooks.html#lifecycle-hooks)


## Vue Lifecycle Hooks

```javascript
Hooks.vue
<template>
  <div>
    <h1>Hello!</h1>
  </div>
</template>

<script>
export default {
  name: "Hooks",
  beforeCreate() {
    // Called after the instance has been initialized
    alert(‘beforeCreate triggered.’);
  },
  created() {
    // Called after the instance is created
    alert(‘created triggered.’);
  },
  beforeMount() {
    // Called right before the mounting begins
    alert(‘beforeMount triggered.’);
  },
  mounted() {
    // Called after the instance has been mounted
    alert(‘mounted triggered.’);
  },
  beforeUpdate() {
    // Called when data changes, before the DOM is patched
    alert(‘beforeUpdate triggered.’);
  },
  updated() {
    /**
     * Called after a data change causes the virtual DOM 
     * to be re-rendered and patched.
     */
    alert(‘updated triggered.’);
  },
  beforeUnmount() {
    // Called right before a component instance is unmounted
    alert(‘beforeUnmount triggered.’);    
  },
  unmounted() {
    // Called after a component instance has been unmounted
    alert(‘unmounted triggered.’);
  }
}
</script>

<style scoped>
</style>

```

## Computed vs Methods: What to use?

**Computed Properties:**
cached based on their reactive dependencies
only re-evaluates when some of its reactive dependencies have changed

**Methods:**
no caching occurs
will always run the function whenever a re-render happens

_TL:DR_
Use Computed Properties when functionality reacts to DOM changes 
Use Methods when functionality is triggered by a user event

`ComputedProperties.vue`

```javascript
<template>
  <div>
    <div>Do I have things to do? {{ thingsToDo }}</div>
    <div>Things to do:</div>
    <ul>
      <li v-for="listItem in sampleList" :key="listItem.text">
        {{ listItem.text }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "ComputedProperties",
  computed: {
    thingsToDo() {
      /**
 * This statement’s result is cached and will only
       * re-evaluate when sampleList changes content,
       * saving computational power.
       */
      return this.sampleList.length > 0 ? 'Yes' : 'No'
    }
  },
  data() {
    return {
      sampleList: [
        { text: 'Eat' },
        { text: 'Sleep' },
        { text: 'Be awesome' }
      ]
    }
  },
}
</script>

<style scoped>
</style>

```

## v-if vs v-show: What to use?

**v-if:**
is "real" conditional rendering
event listeners and child components are properly destroyed and re-created
DOM elements are properly destroyed and re-created
is “lazy”; i.e. DOM elements WILL NOT render unless condition is true on first load

**v-show:**
is always rendered regardless of condition
uses CSS to toggle elements ON and OFF
event listeners and child components stay rendered regardless of condition
DOM elements stay rendered when toggling ON and OFF

_TL:DR_
> Use v-show if you need to toggle something very often
> Use v-if if the condition is unlikely to change at runtime

ConditionalSample.vue

```javascript
<template>
  <div>
    <div>
      <button @click=”toggleContent”>Click Me!</button>
    </div>
    <div v-if=”showContent”>
      <span>I am v-if rendered!</span>
    </div>
    <div v-show=”showContent”>
      <span>I am v-show rendered</span>
    </div>
  </div>
</template>

<script>
export default {
  name: "ConditionalSample",
  data() {
    return {
      showContent: false
    }
  },
  methods: {
    toggleContent() {
      this.showContent = !this.showContent
    }
  }
}
</script>

<style scoped>
</style>

```

## Components

When to use components:
Repeated functionality across multiple pages
Decoupling of long, mangled, spaghetti codebase

Component Data Flow:
To pass data from parent component to child component, use props
To pass data from child component back to parent component, use emit


## How to use components

`ParentComponent.vue`

```javascript
<template>
  <div>
    <div>Hi! I am a parent component.</div>
    <div>
      <ChildComponent />
    <div>
  <div>
</template>

<script>
import ChildComponent from “./ChildComponent”;

export default {
  name: “ParentComponent”,
  components: {
    ChildComponent
  }
}
</script>

<style scoped>
</style>

```

`ChildComponent.vue`

```javascript
<template>
  <div>
    <div>Hi! I am a child component. {{ message }}</div>
  <div>
</template>

<script>
export default {
  name: “ChildComponent”,
  data() {
    return {
      message: ‘yay!’
    }
  }
}
</script>

<style scoped>
</style>

```

## How to pass data to child component

`ParentComponent.vue`

```javascript
<template>
  <div>
    <div>Hi! I am a parent component.</div>
    <div>
      <ChildComponent :message=”message”/>
    <div>
  <div>
</template>

<script>
import ChildComponent from “./ChildComponent”;

export default {
  name: “ParentComponent”,
  components: {
    ChildComponent
  },
  data() {
    return {
      message: ‘yay!’
    }
  }
}
</script>

<style scoped>
</style>

```
`ChildComponent.vue`

```javascript
<template>
  <div>
    <div>Hi! I am a child component. {{ message }}</div>
  <div>
</template>

<script>
export default {
  name: “ChildComponent”,
  props: [
    'message'
  ],
}
</script>

<style scoped>
</style>

```

## How to pass data to parent component

`ParentComponent.vue`

```javascript
<template>
  <div>
    <div>Hi! I am a parent component.</div>
    <div>
      <ChildComponent
        :message=”message”
        @pass-message=”parseChildMessage”
      />
    <div>
  <div>
</template>

<script>
import ChildComponent from “./ChildComponent”;

export default {
  name: “ParentComponent”,
  components: {
    ChildComponent
  },
  data() {
    return {
      message: ‘yay!’
    }
  },
  methods: {
    parseChildMessage(message) {
      alert(‘Yay! Child component says ’ + message);
    }
  }
}
</script>

<style scoped>
</style>

```
`ChildComponent.vue`

```javascript
<template>
  <div>
    <div>Hi! I am a child component. {{ message }}</div>
    <div>
      <button @click=”sendBack”>Click Me!</button>
    </div>
  <div>
</template>

<script>
export default {
  name: “ChildComponent”,
  props: [
    'message'
  ],
  methods: {
    sendBack() {
      this.$emit(‘passMessage’, ‘Hello!’);
    }
  }
}
</script>

<style scoped>
</style>


```

## Vuex

**What it is:**
a state-management pattern + library
a centralized store for all components
has rules ensuring that the state can only be mutated in a predictable fashion
used in building large-scale Single-Page Applications


## Vuex Core Concepts

**State**
Vuex uses a single state tree
a single object contains all application-level state
serves as the "single source of truth."
Vuex follows the same rules as the data in a Vue instance
reactive

**Getters**
considered as computed properties for stores
result is cached based on its dependencies
will only re-evaluate when some of its dependencies have changed

**Mutations**
the only way to actually change state in a Vuex store
mutations are very similar to events
each mutation has a string type and a handler.
handler function is where actual state modifications are performed
handler function receives state as the first argument

**Actions**
are similar to mutations, with key differences
actions commit mutations instead of mutating the state
actions can contain arbitrary asynchronous operations
mutations have to be synchronous, while actions does not have to be

**Modules**

allows Vuex to divide store into modules
each module can contain its own state, mutations, actions, getters

References:
https://vuex.vuejs.org/guide/state.html
https://vuex.vuejs.org/guide/getters.html
https://vuex.vuejs.org/guide/mutations.html
https://vuex.vuejs.org/guide/actions.html
https://vuex.vuejs.org/guide/modules.html

## 
How to Setup Vuex in a Project

`src/store/index.js`

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({})

export default store


src/main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store/index'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store
}).$mount('#app')


```
**How to add store state**

```javascript
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    todoList: [
      { value: ‘Code’ }
    ]
  },
})

export default store
```

`SampleComponent.vue`

```javascript
<template>
  <div>
    <tr v-for="(item, index) in todoList" :key="index">
      <td>{{ item.value }}</td>
    </tr>
  </div>
</template>

<script>
export default {
  name: ‘SampleComponent’,
  computed: {
    todoList() {
      return this.$store.state.todoList
    }
  }
}
</script>

<style scoped>
</style>
```

## How to add store getters

`src/store/index.js`

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    todoList: [
      { value: ‘Code’ }
    ]
  },
  getters: {
    todoList: state => {
      return state.todoList
    }
  },
})

export default store


```
`SampleComponent.vue`

```javascript
<template>
  <div>
    <tr v-for="(item, index) in todoList" :key="index">
      <td>{{ item.value }}</td>
    </tr>
  </div>
</template>

<script>
export default {
  name: ‘SampleComponent’,
  computed: {
    todoList() {
      return this.$store.getters.todoList
    }
  }
}
</script>

<style scoped>
</style>
```


## How to add store mutations

`src/store/index.js`

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    todoList: [
      { value: ‘Code’ }
    ]
  },
  getters: {
    todoList: state => {
      return state.todoList
    }
  },
  mutations: {
    addTodoItem(state, value) {
      state.todoList.push({value: value})
    },
    removeTodoItem(state, value) {
      state.todoList.splice(value, 1)
    }
  }
})

export default store


```
`SampleComponent.vue`

```javascript
<template>
  <div>
    <div>
      <span>Add Item: </span>
      <input v-model="todoInput" />
      <button @click="addItem">Add Item</button>
    </div>
    <div>
      <tr v-for="(item, index) in todoList" :key="index">
        <td>
          <button @click="removeItem(index)">X</button>
        </td>
        <td>{{ item.value }}</td>
      </tr>
    <div>
  </div>
</template>

<script>
export default {
  name: ‘SampleComponent’,
  computed: {
    todoList() {
      return this.$store.getters.todoList
    }
  },
  data() {
    return {
      todoInput: ‘’
    }
  },
  methods: {
    addItem() {
      this.$store.commit('addTodoItem', this.todoInput)
    },
    removeItem(index) {
      this.$store.commit(‘removeTodoItem’, index)
    }
  }
}
</script>

<style scoped>
</style>

```

## How to add store actions

`src/store/index.js`

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    todoList: [
      { value: ‘Code’ }
    ]
  },
  getters: {
    todoList: state => {
      return state.todoList
    }
  },
  mutations: {
    addTodoItem(state, value) {
      state.todoList.push({value: value})
    },
    removeTodoItem(state, value) {
      state.todoList.splice(value, 1)
    }
  },
  actions: {
    addItem (context, item) {
      context.commit('addTodoItem', item)
    },
    removeItem(context, itemIndex) {
      context.commit('removeTodoItem', itemIndex)
    }
  }
})

export default store


```
`SampleComponent.vue`

```javascript
<template>
  <div>
    <div>
      <span>Add Item: </span>
      <input v-model="todoInput" />
      <button @click="addItem">Add Item</button>
    </div>
    <div>
      <tr v-for="(item, index) in todoList" :key="index">
        <td>
          <button @click="removeItem(index)">X</button>
        </td>
        <td>{{ item.value }}</td>
      </tr>
    <div>
  </div>
</template>

<script>
export default {
  name: ‘SampleComponent’,
  computed: {
    todoList() {
      return this.$store.getters.todoList
    }
  },
  data() {
    return {
      todoInput: ‘’
    }
  },
  methods: {
    addItem() {
      this.$store.dispatch('addItem', this.todoInput)
    },
    removeItem(index) {
      this.$store.dispatch(‘removeItem’, index)
    }
  }
}
</script>

<style scoped>
</style>


```

## How to setup store into modules

`src/store/index.js`

```javascript
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const moduleA = {
  state: () => ({
    todoList: [
      { value: ‘Code’ }
    ]
  }),
  getters: {
    todoList: state => {
      return state.todoList
    }
  },
  mutations: {
    addTodoItem(state, value) {
      state.todoList.push({value: value})
    },
    removeTodoItem(state, value) {
      state.todoList.splice(value, 1)
    }
  },
  actions: {
    addItem (context, item) {
      context.commit('addTodoItem', item)
    },
    removeItem(context, itemIndex) {
      context.commit('removeTodoItem', itemIndex)
    }
  }
}

const store = new Vuex.Store({
  modules: {
    moduleA: moduleA
  } 
})

export default store
```


`SampleComponent.vue`

```javascript
<template>
  <div>
    <div>
      <span>Add Item: </span>
      <input v-model="todoInput" />
      <button @click="addItem">Add Item</button>
    </div>
    <div>
      <tr v-for="(item, index) in todoList" :key="index">
        <td>
          <button @click="removeItem(index)">X</button>
        </td>
        <td>{{ item.value }}</td>
      </tr>
    <div>
  </div>
</template>

<script>
export default {
  name: ‘SampleComponent’,
  computed: {
    todoList() {
      return this.$store.getters.todoList
    }
  },
  data() {
    return {
      todoInput: ‘’
    }
  },
  methods: {
    addItem() {
      this.$store.dispatch('addItem', this.todoInput)
    },
    removeItem(index) {
      this.$store.dispatch(‘removeItem’, index)
    }
  }
}
</script>

<style scoped>
</style>

```

## Vue Router

**What it is:**
a routing mechanism for switching components
used in building Single Page Applications
useful for changing components using URL paths

**Reference:** https://router.vuejs.org/guide/#html

## 
How to Setup Vue Router in a Project

`src/routes/index.js`

```javascript
import Vue from 'vue'
import VueRouter from 'vue-router'
import HelloWorld from './../components/HelloWorld.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/', 
    component: HelloWorld,
  },
]

const router = new VueRouter({
  routes
})

export default router


```
`src/main.js`

```javascript
import Vue from 'vue'
import App from './App.vue'
import router from './routes/index.js'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

```

`src/App.vue`

```javascript
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
      <router-link :to="{ path: '/'}">Home</router-link>
    </div>
    <router-view />
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

<style scoped>
</style>


```

## How to add routes

`src/routes/index.js`

```javascript
import Vue from 'vue'
import VueRouter from 'vue-router'
import HelloWorld from './../components/HelloWorld.vue'
import TodoComponent from './../components/TodoComponent.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/', 
    component: HelloWorld,
  },
  {
    path: '/todolist', 
    component: TodoComponent
  },
]

const router = new VueRouter({
  routes
})

export default router


```
`src/components/TodoComponent.vue`

```javascript
<template>
  <div>
    Todo Component
  </div>
</template>

<script>
export default {
	name: 'TodoComponent'
}
</script>

<style scoped>
</style>


```
`src/App.vue`

```javascript
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <div>
      <router-link :to="{ path: '/'}">Home</router-link>
      <router-link 
        :to="{ path: '/todolist'}"
      >
        TodoComponent
      </router-link>
    </div>
    <router-view />
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

<style scoped>
</style>


```
**Dynamic Routes**

**What it is:**
useful for components which depends on certain parameters before rendering
allows passing values in between routes

## How to add dynamic routes

> To see it in action, type the following URL:
http://localhost:8080/#/person/1

`src/routes/index.js`

```javascript
import Vue from 'vue'
import VueRouter from 'vue-router'
import HelloWorld from './../components/HelloWorld.vue'
import TodoComponent from './../components/TodoComponent.vue'
import PersonComponent from './../components/PersonComponent.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/', 
    component: HelloWorld,
  },
  {
    path: '/todolist', 
    component: TodoComponent
  },
  {
    path: ‘/person/:id’,
    component: PersonComponent
  }
]

const router = new VueRouter({
  routes
})

export default router


```
`src/components/PersonComponent.vue`

```javascript
<template>
  <div>
    Person Component
    {{ $route.params.id }}
  </div>
</template>

<script>
export default {
	name: 'PersonComponent'
}
</script>

<style scoped>
</style>

```


## Programmatic Navigation

**When to use:**
route needs to be navigated using function
route requires dynamic parameters
route needs to be navigated after a certain process occurs

**Reference: **https://router.vuejs.org/guide/essentials/navigation.html

## How to programmatically navigate a route

ProgrammaticRouting.vue

```javascript
<template>
  <div>
    <button @click=”navigate”>Navigate to somewhere</button>
  </div>
</template>

<script>
export default {
  name: 'ProgrammaticRouting',
  methods: {
    navigate() {
      // The path must be the same as the path declared in routes
      this.$router.push({path: ‘somewhere’})
    }
  }
}
</script>

<style scoped>
</style>


```