Vue Bulgaria
DOBROMIR HRISTOV
WORKSHOP
BEGINNERS
Who am I ?
Dobromir Hristov
- Lead FE Developer @Hypefactors
- Over 3 years of Vue experience
- Article author
- Open Source author
- Vue Ecosystem Contributor
- Vue Community Leader
Preparation for the Workshop
0
0
Preparation for the Workshop
What are we building?
We will build a simple multi page Vue SPA in the form of a dynamic map of Westeros from Game of Thrones.
The map will be populated by the 9 great houses, each in its respective place.
Each house will have a drop down menu with a few of its main members.
Each member will have their own personal page.
0
Preparation for the Workshop
- The minimum requirements needed to build a Vue.js powered SPA.
- We will dive into the basics like directives, methods and computed properties.
- We will look into more interesting topics, like what Vue Components and Single File Components using Vue CLI.
- The final chapter will introduce Multi page routing with Vue Router
What will we learn?
0
Preparation for the Workshop
- Have Node and NPM installed - only for last 2 chapters
- Have a recent version of Firefox or Chrome
- Install the Vue Devtools on your browser
- Allow access to file URLs for Chrome
- Favorite Code Editor - Webstorm, Sublime, VSCode, Atom
- Download the Vue Cheat Sheet
- Download repository - https://github.com/dobromir-hristov/vue-beginner-workshop
Before we start
0
Preparation for the Workshop
- Describe Vue features and show usage examples.
- Explain chapter challenges from Challenge Details markdown
- Lecturer live codes first part of challenges and participants copy.
- Participants solve the second part of the challenges.
- Lecturer live codes second part of the challenges, explaining good practices, while attendees correct their mistakes.
- Debrief.
- Easter Eggs
Workshop Workflow
Introduction to
Vue
I
I
Introduction to Vue
- Modern
- Progressive
- Composable
- Core - View Only
- Declarative
- Reactive
What is Vue?
I
Introduction to Vue
- Small - 21kb
- Performant - Virtual Dom
- Approachable - little JS knowledge
- Versatile - incrementally adaptable with libs
Key Features
- Documented
- Flexible - SASS, TS, Pug
- Official libraries- Vuex, VueRouter
- Transition System
I
Introduction to Vue
The Vue Instance
....
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
{{ vueText }}
</div>
<script>
var app = new Vue({
el: '#app',
data: {
vueText: 'Hello Vue'
}
})
</script>
</body>
</html>
<div id="app">
Hello Vue
</div>
I
Introduction to Vue
Basic Example
<div id="app">
<div>
{{ vueText }}
</div>
<input type="text" v-model="vueText">
</div>
I
Introduction to Vue
Challenges
- Use the 1-introduction/base/index as a base.
- Define a dynamic data property inside the new Vue instance's data object.
- Display the newly defined data property in the template using moustache syntax - {{ dataProperty }}
- Test in the Browser
Directives and
Rendering Data
II
II
Directives and Rendering Data
- v-if
- v-else
- v-for
- v-model
- v-show
- v-bind
- v-on
- v-html
What is a Directive?
<div v-if="isVisible === true"> That's hot! </div>
- Special attributes that begin with "v-" prefix
- Attribute value is a single JavaScript expression
- Automatically apply side effects to the DOM when the value of its expression changes
Expression Body
Directive Name
II
Directives and Rendering Data
V-IF vs V-SHOW
<div v-if="false">
Invisible div?
</div>
<div v-show="false">
Invisible div?
</div>
<!---->
<div style="display: none">
Invisible div?
</div>
Conditionally display elements in the DOM
Hides the element
Removes the element
II
Directives and Rendering Data
V-FOR
- Renders the DOM element that it is attached to, for each property in the data collection that is looped
- Works with Arrays, Objects and Numbers
<div class="pepper" v-for="chilli in chillies">
{{ chilli }}
</div>
// the Vue Instance
data: {
chillies: [
'Cayenne',
'Habanero',
'Jalapeno',
'Ghost Pepper',
'Trinidad Moruga Scorpion',
'Carolina Reaper'
]
}
We love extremely hot food!
II
Directives and Rendering Data
V-BIND
Dynamically binds DOM element attributes to data on the Vue instance
<button :disabled="isSaving === true">
Save Form
</button>
<button disabled="disabled">
Save Form
</button>
v-bind:property has a shorthand as just :property
data: {
isSaving: true
}
II
Directives and Rendering Data
V-BIND Examples
<button :disabled="isActive" :class="userEnergyLevel">
Get in Shape
</button>
<div :class="{ active: isActive }"> Sports! </div>
<div :class="[ favoriteSport, isActive ? 'active': 'passive' ]">
Activity
</div>
<!-- assign multiple at once -->
<div v-bind="{ class: { active: isActive }, title: 'Healthy' }">
Running is fun.
</div>
<button disabled="disabled" class="low">
Get in Shape
</button>
<div class="active"> Sports! </div>
<div class="rugby active">
Activity
</div>
<!-- assign multiple at once -->
<div class="active" title="Healthy">
Running is fun.
</div>
data: {
isActive: true,
userEnergyLevel: 'low',
favoriteSport: 'rugby'
}
II
Directives and Rendering Data
V-MODEL
Creates a two way bond between a form element and data property on the instance
<form>
<label>Recipe Name</label>
<input v-model="form.name" type="text">
<label>Portions</label>
<input
v-model="form.portions"
type="number">
<label>Vegan
<input
v-model="form.isVegan"
type="checkbox">
</label>
<label>Difficulity</label>
<input
v-model="form.difficulity"
type="range">
</form>
II
Directives and Rendering Data
Directive Gotcha's
- V-model - always predefine your data properties.
- V-bind shorthand - always add semicolon to short syntax.
- V-for - Define :key binding with an unique value.
- V-else can only after v-if.
II
Directives and Rendering Data
Challenges
- Use the 2-directives-rendering/base/index as base.
- Add the .map element to the page.
- Add a .house and bind its class to houses.stark.name. Transform it to toLowerCase. Check in Browser
- Add an img tag inside the .house and bind it's src to the houses.stark.sigil property. Check in Browser - should see Stark sigil.
- Loop over the houses on .house and replace the class and src bidings to use the looped item.
15 Min Break
Events
III
III
Events
Events in Vue
- Events can be Native or Custom Vue events
- Events are handled using v-on directive
v-on:click has a shorthand as just @click
<button @click="counter++">
Increment
</button>
Expression
Event Name
III
Events
Events in the Wild
<div class="vueDog vuePet"
@click="vueDog = !vueDog">
<img v-if="vueDog"
src="vueDog.jpeg">
<div class="text"> Tap the doggy </div>
</div>
<div class="vueReprtile vuePet"
@mouseenter="vueReptile = true"
@mouseleave="vueReptile = false">
<img
v-show="vueReptile"
src="vueReptile.jpg">
<div class="text"> Touch the gecko </div>
</div>
<div class="vueReprtile vuePet"
@mousedown="vueLeo = true"
@mouseup="vueLeo = false">
<div class="text"> Keep finger on the leo </div>
<img v-show="vueLeo"
src="https://image.ibb.co/cD9xMV/Vue-Leopard.jpg">
</div>
III
Events
Event modifiers
- Take care of repetitive event specific actions that are easy to forget, like event.preventDefault.
- Can augment the event handler behavior.
- Makes event handlers responsible for handling event only
- @click.prevent - Executes event.preventDefault.
- @click.stop - Executes event.stopPropagation.
- @click.self - Handles events emitted only by the element it is attached to and not its children.
- @click.once - Executes only once.
- @click.ctrl - Executes only with combination with ctrl key.
III
Events
V-MODEL explained
<input type="text" v-model="username">
<input
:value="username"
type="text"
@input="username = $event.target.value"
>
Intelligently handles different form element components by listening and handling data in the required way
<input type="checkbox" v-model="isVisible">
<input
:checked="isVisible"
type="checkbox"
@change="isVisible = $event.target.checked"
>
Expand under the hood to
III
Events
Vue Lifecycle Diagram
III
Events
Event Gotcha's
- @event.stop - To stop event propagation (bubbling)
- @event.prevent - To stop event from triggering default action.
- @submit - Add on forms to prevent page refresh when submitting.
- $event - Use in template event expressions to access event payload.
III
Events
Challenges
- Create a members dropdown with each house member.
- Show the members dropdown when clicking on a house.
- Hide the members dropdown when clicking away.
Methods and Computed Properties
IV
IV
Methods and Computed Properties
What are Methods
- Functions that are bound to the Vue instance.
- Useful for extracting complex logic away from directives.
-
Make templates easier to read and manage.
- Methods have access to all the instance properties.
- Methods can be used inside Lifecycle Hooks and Watchers.
-
Can be used inside moustache expressions or dynamic bindings
data: {
counter: 0
},
methods: {
increment() {
this.counter = this.counter + 1
}
}
<button @click="increment">Add one</button>
<div>{{ counter }}</div>
IV
Methods and Computed Properties
Method Examples
<button @click="increment">+1 boringness</button>
<button @click="incrementBy(10)">+10 boringness</button>
<button @click="incrementTimes(5)">x5 boringness</button>
data: {
boringMeter: 0
},
methods: {
increment(){
this.incrementBy(1)
},
incrementBy(number = 0){
this.boringMeter = this.boringMeter + number
},
incrementTimes(times = 2) {
this.boringMeter = this.boringMeter * times
}
}
IV
Methods and Computed Properties
What are Computed Properties
- Functions that act as dynamic property getters on the Vue instance.
- Recomputed every time any of its inner dependency values is changed.
- Cached to be very performant.
- Computed properties are bound to the Vue instance and have access to data properties, methods and other computed properties
// ...
data: {
firstName: 'John',
lastName: 'Doe'
},
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
},
<div>{{ fullName }}</div>
<div>John Doe</div>
IV
Methods and Computed Properties
Computed Properties Examples
<input v-model="search" type="text">
<div
v-for="chilli in filteredChillies"
:key="chilli"
class="pepper"
>
{{ chilli }}
</div>
data: {
search: "",
chillies: [ "Cayenne", "Habanero", ... ]
},
computed: {
filteredChillies() {
return this.chillies.filter(chilly => {
return chilly
.toLowerCase()
.includes(this.search)
}
);
}
IV
Methods and Computed Properties
Computed Properties vs Methods
- Executed on dependency change
- Cached
- Does not take parameters
- Must be sync
- Support defining a Getter/Setter.
- Should never mutate other properties in getters.
- Executed every time in templates
- Not cached
- Takes parameters
- Can be async
- Can return other functions or data types.
- Can mutate other properties or call other functions
IV
Methods and Computed Properties
Gotcha's
- Computed Property must return a value!
- Computed Property cannot be called as a function.
- Computed Property cannot be overwritten. Need to define Get/Set pair to do so.
- Computed Properties, Methods and data properties cannot share the same name.
- Be careful with this context when working with Methods and callbacks
IV
Methods and Computed Properties
Challenges
- Transform the @click expression on .house to a method.
- Hide the .members dropdown when you click on .map
- Transform the visibility check on the .members div to a isVisible method that accepts a houseId parameter.
15 Min Break
Components, Props, Custom Events
V
V
Components, Props, Custom Events
What are Vue Components
- Reusable Vue instances with a name.
- Tend to be small, isolated and testable.
- Accept same parameters as main Vue instance.
- Can accept properties, called Props.
- Can emit Events.
- Can be registered as Global or Local components
V
Components, Props, Custom Events
Composing Components
Vue Component Definition
var counter = {
template: '<button @click="increment">{{ count }}</button>',
data() {
return {
count: 0
}
},
methods: {
increment(){
this.count = this.count + 1
}
}
}
Vue.component('VCounter', counter) // Global component registration
<div>
<v-counter></v-counter>
</div>
V
Components, Props, Custom Events
Vue Component example
var VCounter = {
template: '<button @click="increment">{{ count }}</button>',
data() {
return {
count: 0
}
},
methods: {
increment(){
this.count = this.count + 1
}
}
}
new Vue({
el: '#app',
components: {
VCounter: VCounter // Local Component registration
}
})
V
Components, Props, Custom Events
Types of Templates
var component = {
data: () => ({ title: '' }),
template: '<div class="someClass">{{title}}</div>'
}
<script type="text/x-template" id="unique-id">
<div class="someClass">
{{ title }}
</div>
</script>
<script>
var component = {
data: () => ({ title: '' }),
template: '#unique-id'
}
</script>
String or template literal
X-template template tag
V
Components, Props, Custom Events
const component = {
data: () => ({ title: '' }),
render() {
return <div class="someClass">{ this.title }</div>
}
})
JSX
const component = {
data: () => ({ title: '' }),
render(createElement) {
return createElement('div', {
class: ['someClass']
},
this.title)
}
}
Render Function
Vue Component Specifics
- Components can be registered globally or locally.
- Can be nested inside other components.
- Each component's state is isolated from other components.
- A component can have multiple instances on a page.
- Components receive data from parents via Props.
- Components send data to parents via Events.
V
Components, Props, Custom Events
Component Gotcha's
- Template - only one top level element.
- Data - must be function returning an object.
- Cannot* access state or properties of other components.
- Name must not collide with HTML5 elements.
- Name convention- TheCounter, VCounter, VueCounter, BaseCounter, AppCounter etc.
- Use <kebab-case> to write component tags. <PascalCase> only in CLI.
V
Components, Props, Custom Events
What are Props
- Way to pass data from parent down to child components.
- Dynamic data is passed via v-bind syntax.
- Props can have type validation.
- Props can have default values
- Props can have custom validation
V
Components, Props, Custom Events
<counter :min="5" :step="5" :max="100"></counter>
Ways of Defining Props
V
Components, Props, Custom Events
props: ['PropA', 'PropB']
props: {
PropA: Object,
PropB: String
}
props: {
PropA: {
type: Object,
required: true
},
PropB: {
type: String,
default: 'Lorem Ipsum'
}
}
Shortest - mostly for demos
Slightly Longer
Longest - recommended, most detailed
Prop example
<v-counter :increment-by="5"></v-counter>
{
template: '<button @click="increment">{{ count }}</button>',
props: {
incrementBy: {
type: Number,
default: 1
}
},
data() {
return { count: 0 }
},
methods: {
increment(){
this.count = this.count + this.incrementBy
}
}
}
V
Components, Props, Custom Events
Prop Gotcha's
V
Components, Props, Custom Events
-
Props are considered immutable to child components.
-
Default value for a prop of type Object or Array should be a factory function returning the required data.
-
Use as kebab-case in templates, but as camelCase inside the component instance.
What are Custom Events
Way for child components to communicate with parent components.
V
Components, Props, Custom Events
Working with Custom Events
V
Components, Props, Custom Events
template: '<button @click="notifyParent">click me</button>',
data: () => ({ count:0 }),
methods: {
notifyParent() {
this.$emit('change', this.count)
}
}
<counter @change="handleChange"/>
methods: {
handleChange(count) {
// handle event
}
}
Parent Component
Child Component
- Events can pass data as payload.
- Emitted via $emit('event-name') from child components.
- Handled in parent component via events i.e. @event-name="action".
Custom Event Example
V
Components, Props, Custom Events
Custom Event Code Example
V
Components, Props, Custom Events
{
props: {
incrementBy: {
type: Number,
default: 1
},
value: {
type: Number,
required: true
}
},
methods: {
increment(){
const value = this.value + this.incrementBy
this.$emit('input', value)
}
}
}
<v-counter
:increment-by="5"
:value="currentCount"
@input="handleInput"
></v-counter>
Child Component
Parent Component
{
data() {
return {
currentCount: 0
}
},
methods: {
handleInput(emittedValue){
this.currentCount = emittedValue
}
}
}
<button @click="increment">{{value}}</button>
Event Gotcha's
V
Components, Props, Custom Events
- Always use kebab-case for event names. They are transformed to lower case in templates (HTML5 is case insensitive)
- Native event payload is an HTML Event. Vue Events carry what you pass as payload, can be any primitive type.
- Custom events do not bubble up the parent chain like normal events.
Challenges
- Extract .house element into a House component
- Move isVisible from the Vue instance to a computed property on House.
- $emit a house-change event on @click.stop on the .house.
- Extract .members element into a Members component
- Bonus: Extract .member element into a Member component
V
Components, Props, Custom Events
15 Min Break
Vue CLI and Single File Components
VI
VI
Vue CLI and Single File Components
What is Vue CLI
- Complex set of build tools for fast and painless app development
- Feature rich - Babel, TypeScript, ESLint, PostCSS, PWA, Unit Testing & End-to-end Testing
- Extensible - allows installing external plugins
- Instant Prototyping - build and run Vue files without scaffolding projects
npm install -g @vue/cli
vue create my-project
VI
Vue CLI and Single File Components
Project creation and augmentation
- User is asked to pick options on project bootstrap
- Choices can be saved as a preset for later
- New features or plugins can be enabled at a later phase
VI
Vue CLI and Single File Components
Vue CLI GUI
- Visual interface to manage Vue Projects.
- Allows adding, removing, augmenting plugins.
- Allows managing npm dependencies.
- Allows running tasks like Webpack Dev server, Lint, etc.
vue ui
Vue Single File Components
<template>
<button class="v-button" @click="inc">{{ count }}</button>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
inc() {
this.count = this.count + 1
}
}
}
</script>
<style lang="scss">
.v-button {
background: $red;
padding: $button-padding;
}
</style>
VI
Vue CLI and Single File Components
- Files with .vue extension.
- Template, script and style are in one file.
Why use .vue files?
- Encourage writing small, isolated modules.
- Can use latest ECMAScript specifications via Babel
- Ability to use pre-processors - TypeScript, SASS, Less, Stylus, Pug.
- Allows using post-processors - Autoprefixer
- Proper template syntax highlighting
- Allows for scoping css styles to component only
- Async component loading possibilities
VI
Vue CLI and Single File Components
Why precompilation is necessary.
- Browsers don't know what .vue extension is.
- Need to compile to .js files.
- Compilation requires a configured Webpack build chain.
- Using latest ECMAScript features requires transpilation based on target browser.
- Browsers cannot* resolve separate components dynamically.
- PostCSS and style preprocessors need extra configuration.
All of the above are solved by Vue CLI out of the box.
VI
Vue CLI and Single File Components
Challenges
- Move the Main Vue Instance template and instance properties over to App component
- Import the houses data from src/data/houses.js to App component
- Move House, Members and Member* components each to a separate SFC .vue file inside the components folder.
VI
Vue CLI and Single File Components
Vue Plugins and Vue Router
VII
VII
Vue Plugins and Vue Router
What are Plugins
- Distributable bundles that extend Vue's functionality.
- Can be used both in Browser and in Bundle mode.
- Can register global components.
- Can attach properties to the Vue prototype.
- Can add extra functionality to components.
Vue Plugins and Vue Router
What is Vue Router
- Official Vue multi-page routing plugin
- Allows for website navigation without refresh in the Browser.
- Connects URL structure to component definitions - Pages
- Pages are defined in a Routes list
- Pages can have dynamic parameters in URL user/:userId
VII
Vue Plugins and Vue Router
Vue Router Instance
// 1. Define some routes
const routes = [
{ path: '/', component: HomePage },
{ path: '/user/:id', name: 'users', component: UserPage, props: true }
]
// 2. Pass the routes
const router = new VueRouter({
routes: routes
})
var app = new Vue({
el: '#app',
router: router
})
website.com/user/:id
website.com/user/1
VII
Vue Plugins and Vue Router
Child Pages
- Each page can have many subpages.
- URL can extend from it's parent or have separate structure.
- Child pages are rendered only inside the router-view custom component of parent pages.
- Child page change does not affect the parent component.
- Child pages can receive props from URL params, route config, and parents.
<div id="app">
<the-header></the-header>
<div class="main-wrapper">
<router-view></router-view>
</div>
<the-footer></the-footer>
</div>
<div id="app">
<the-header></the-header>
<div class="main-wrapper">
<!-- child is injected -->
<user-page></user-page>
<!-- parent template continues -->
</div>
<the-footer></the-footer>
</div>
VII
Vue Plugins and Vue Router
Router Link
- Dynamic component used for creating links to pages defined in the router config.
- Auto registered by Vue Router
- Can intercept clicks to stop browser from refreshing the page.
<router-link to="/about">Go to About</router-link>
<router-link :to="{ name: 'about' }">
Go to About
</router-link>
<router-link :to="{ name: 'user', params: { id: 1 } }">
Go to About
</router-link>
<a href="/about">Go to About</a>
<a href="/about">Go to About</a>
<a href="/user/1">Go to User 1</a>
VII
Vue Plugins and Vue Router
History vs Hash
- Requires server setup to redirect all requests to index.html
- Uses HTML5 History Api
- Nice, SEO friendly URLs.
- Id navigation for sections is possible.
- Default mode
- Just works
- Adds # to routes
- Can conflict with id navigation to page sections
vuebulgaria.com/stark/eddard-stark
vuebulgaria.com/#/stark/eddard-stark
VII
Vue Plugins and Vue Router
Challenges
-
Define a component called WesterosMapPage that will represent the home page.
-
Copy App.vue's logic and template to the component.
-
Assign it as home with / path on the router.
-
- Change the main Vue instance’s template to a single router-view
- Create a MemberPage component
- Assign it as a page with name of member, path /:houseId/:memberId
- Add a router-link linking to the MemberPage on the Members template.
- Display the currentMember's name on the MemberPage template
- Add a back button to the Westeros Map.
VII
Special thanks to the Vue Bulgaria Team
Nedyalko
Dyakov
Elena
Gancheva
Hristiyan
Ivanov
Find me at
dobromir-hristov
d_m_hristov
dobromir_hristov
dobromir-hristov
Dobromir Hristov
Where to next?
Vue Mastery
Vue School
Vue JS 2 - The Complete Guide
Learn Vue 2: Step By Step
Vue Beginners Workshop v2
By Dobromir Hristov
Vue Beginners Workshop v2
- 2,063