marp | theme | style |
---|---|---|
true |
gaia |
section.lead h1 {
text-align: center;
}
section.middle li{
text-align: center;
}
|
- A progressive framework for building user interfaces
- No need to make multiple requests to the server
- very lean and lightweight (only 20kb minified)
- A Vue.js instance is created by calling the
Vue
function- an instance can controller several components
- it controls the data, methods, and lifecycle of the components
new Vue({})
is the syntax for creating a Vue.js instance
el
- element to mount the instance to. root element!el: '#app'
data
- data object to be used in the instancedata: {}
methods
- methods to be used in the instancemethods: {}
computed
- computed properties to be used in the instancecomputed: {}
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage() {
this.message = this.message.split('').reverse().join('')
}
}
}
})
this
refers to the Vue.js instance- data is accessed using
this
in the instance - for example in methods,
this.message
refers to the data object
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage() {
this.message = this.message.split(''+n('')
}
}
}
})
- we can bind data to the DOM using
v-bind
or:
in the template - we can bind data to the DOM using
v-on
or@
in the template
<div id="app">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
<a :href="url">Vue.js</a>
</div>
<script>
new Vue({
el: '#app',
data() return {
message: 'Hello Vue.js!',
url: 'https://vuejs.org/'
}
})
</script>
- we can add html elements to the DOM using
v-html
in the template
<div id="app">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
<a :href="url">Vue.js</a>
<div v-html="html"></div>
</div>
<script>
new Vue({
el: '#app',
data() return {
message: 'Hello Vue.js!',
url: 'https://vuejs.org/',
html: '<h1>Vue.js</h1>'
})
</script>
- we can add event modifiers to the DOM using
v-on
in the template or@
in the template - events are triggered when the user interacts with the DOM
- some list of event modifiers
v-on:click.once
- triggers when the user clicks on the element once and only oncev-on:click.prevent
- prevents the default behavior of the event
- some list of keyboard event modifiers
v-on:keyup.enter
- triggers when the user presses the enter keyv-on:keyup.esc
- triggers when the user presses the escape keyv-on:keyup.tab
- triggers when the user presses the tab keyv-on:keyup.delete
- triggers when the user presses the delete keyv-on:keyup.backspace
- triggers when the user presses the backspace keyv-on:keyup.space
- triggers when the user presses the space keyv-on:keyup.up
- triggers when the user presses the up arrow keyv-on:keyup.down
- triggers when the user presses the down arrow keyv-on:keyup.left
- triggers when the user presses the left arrow keyv-on:keyup.right
- triggers when the user presses the right arrow key- multiple modifiers can be used together
v-on:keyup.enter.space
- triggers when the user presses the enter and space keyv-on:keyup.enter.space.prevent
- triggers when the user presses the enter and space key and prevents the default behavior of the event
- we can bind data to the DOM using
v-model
in the template v-model
is a shorthand forv-bind:value
andv-on:input
v-model
is used for input elements likeinput
,textarea
, andselect
this will update the data when the user interacts with the DOM
<div id="app">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">Reverse Message</button>
<a :href="url">Vue.js</a>
<div v-html="html"></div>
<input type="text" v-model="message">
</div>
<script>
new Vue({
el: '#app',
data() return {
message: 'Hello Vue.js!',
url: 'https://vuejs.org/',
html: '<h1>Vue.js</h1>'
})
</script>
computed
is an object that contains the computed properties- every time the data changes, the computed properties are re-evaluated
- computed properties are only re-evaluated when their dependencies change
<script>
export default {
name: 'App',
data() {
return {
value1: 0,
value2: 0} },
computed: {
totalSum() {
console.log('totalSum')
return this.value1 + this.value2}}}
</script>
- we can add dynamic CSS classes to the DOM using
v-bind:class
in the template or:
in the template - it take in key value pairs where the key is the CSS class and the value is a boolean
- example if
isActive
is true, the CSS classactive
will be added to the DOM
<div v-bind:class="{ active: isActive }"></div>
<div :class="{ active: isActive }"></div>
- we can add conditional rendering to the DOM using
v-if
in the template v-if
is used to render the DOM if the condition is truev-else-if
is used to render the DOM if the condition is truev-else
is used to render the DOM if the condition is truev-show
is different fromv-if
in thatv-show
will always render the DOM but will use the CSS propertydisplay: none
to hide the DOM
<div v-if="isActive">Active</div>
<div v-else-if="isInactive">Inactive</div>
<div v-else>Unknown</div>
<div v-show="isActive">Active</div>
- we can add looping to the DOM using
v-for
in the template v-for
is used to render the DOM for each item in the arrayv-for
can also be used to render the DOM for each item in the object
<div v-for="item in items" :key="item.id">{{ item.name }}</div>
<div v-for="(item, index) in items" :key="item.id">{{ index }} - {{ item.name }}</div>
<div v-for="(value, key, index) in object" :key="index">{{ index }} - {{ key }} - {{ value }}</div>
- we can create multiple vue instances using
new Vue()
- each vue instance will have its own data, methods, computed properties, and lifecycle hooks
var app1 = new Vue({
el: '#app1',
data: {
message: 'Hello Vue.js!'
}
})
var app2 = new Vue({
el: '#app2',
data: {
message: 'Hello Vue.js!'
}
})
- we can create reusable components using
Vue.component()
- components are reusable Vue instances with a name
- components can be registered globally or locally
- to register a component globally, we use
Vue.component()
- to register a component locally, we use
components
in theVue
instance
// global component
Vue.component('app-servers', {
template: '<h1>Hello Vue.js!</h1>'
})
// local component
new Vue({
el: '#app',
components: {
'app-servers': {
template: '<h1>Hello Vue.js!</h1>'
}
}
})
- we can reference the DOM using
$ref
in the template $ref
is used to reference the DOM element- to get 'yourAddress' from the DOM, we can use `this.$refs.yourAd
- dress.value`
<input type="text" ref="yourAddress">
<script>
export default {
name: 'App',
methods: {
submit() {
console.log(this.$refs.yourAddress.value)
}
}
}
</script>
- Vue CLI is a command line interface for Vue.js
- Uses ES6 syntax
- compile and bundle the code
- Single file template
- Hot reloading
- for large projects we split the components into smaller components
- we can nest components inside other components
- we need to register the nested components in the parent component
- we can register the nested components globally or locally
- locally
components: { 'app-servers': AppServers }
- globally
Vue.component('app-servers', AppServers)
- locally
- we can register the nested components globally or locally
- Issue
- when we style the components, the styles are applied globally and can affect other components
- Solution
- we can scope the styles to the component using
scoped
in the style tag scoped
will only apply the styles to the component and not to the other components
- we can scope the styles to the component using
<style scoped>
.red {
color: red;
}
</style>
- Issue
- we have a component that has a list of servers
- we have a component that has a list of users
- we want to reuse the list of servers and users in the other components
- it will be easier to maintain the code if we reuse the components
- Solution
- we can create a component that has a list of servers
- we can create a component that has a list of users
- we can reuse the components in the other components
- Nested components are called in template using
<name-of-component></name-of-component>
<template>
<div>
<app-servers></app-servers>
<app-users></app-users>
</div>
</template>
<script>
import AppServers from './AppServers'
import AppUsers from './AppUsers'
export default {
name: 'App',
components: {
'app-servers': AppServers,
'app-users': AppUsers
}
}
</script>
Here is a simple flow chart:
graph TD;
RootComponent-->Header;
RootComponent-->Footer;
RootComponent-->Content;
Content-->ListOfOrders;
Header-->Logo;
Header-->Navigation;
Footer-->SocialMedia;
Footer-->CopyRightsNotice;
- Problem
- We have several components that have the same data
- We want to reuse the components
- We want to pass the data to the components
- Solution
- We can pass data to the components using
props
<template>
<div>
<app-servers :servers="servers"></app-servers>
<app-users :users="users"></app-users>
</div>
</template>
<script>
import AppServers from './AppServers'
import AppUsers from './AppUsers'
export default {
name: 'App',
components: {
'app-servers': AppServers,
'app-users': AppUsers
},
data() {
return {
servers: [
{ id: 1, name: 'Server 1' },
{ id: 2, name: 'Server 2' },
{ id: 3, name: 'Server 3' }
],
users: [
{ id: 1, name: 'User 1' },
{ id: 2, name: 'User 2' },
{ id: 3, name: 'User 3' }
]
}
}
}
<template>
<div>
<h1>Servers</h1>
<ul>
<li v-for="server in servers" :key="server.id">{{ server.name }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'AppServers',
props: ['servers']
}
</script>
- Problem
- when we pass data to the components, we can pass any data type to the components
- we want to validate the data type of the props
- Solution
- we can validate the data type of the props using
props
- we can validate the data type of the props using
type
- we can validate the data type of the props using
required
- we can validate the data type of the props using
default
- we can validate the data type of the props using
<template>
<div>
<h1>Servers</h1>
<ul>
<li v-for="server in servers" :key="server.id">{{ server.name }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'AppServers',
props: {
servers: {
type: Array,
required: true
default: []
}
}
}
</script>
- Primitive Types
- String
- Number
- Boolean
- Null
- Undefined
- Symbol
- Reference Types
- Object
- Array
- Function
// primitive type
let a = 1
let b = a
a = 2
console.log(a) // 2
console.log(b) // 1
// reference type
let a = { name: 'a' }
let b = a
a.name = 'b'
console.log(a.name) // b
console.log(b.name) // b
- Problem
- we want to pass data from the child component to the parent component
- say we have a component that has a list of employees
- we want to pass the selected employee to the parent component
- Solution
- we can pass data from the child component to the parent component using
events
$emit
@event-name
<template>
<div>
<h1>Employees</h1>
<ul>
<li v-for="employee in employees" :key="employee.id" @click="selectEmployee(employee)">{{ employee.name }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'AppEmployees',
props: {
employees: {
type: Array,
required: true,
default: []
}
},
methods: {
selectEmployee(employee) {
this.$emit('employee-selected', employee)
}
}
}
</script>
// parent component
<template>
<div>
<app-employees :employees="employees" @employee-selected="employeeSelected"></app-employees>
</div>
</template>
<script>
import AppEmployees from './AppEmployees'
export default {
name: 'App',
components: {
'app-employees': AppEmployees
},
data() {
return {
employees: [
{ id: 1, name: 'Employee 1' },
{ id: 2, name: 'Employee 2' },
{ id: 3, name: 'Employee 3' }
]
}
},
methods: {
employeeSelected(employee) {
console.log(employee)
}
}
}
</script>
- Problem
- listening to events from a component is not a good practice, and can lead to spaghetti code
- we want to have a global event bus, i.e a global event listener
- Solution
- we can create a global event bus using
Vue
Vue.prototype
$on
$emit
$off
$once
$off
- we can create a global event bus using
- Problem
- we want to execute some code when a component is created
- we want to execute some code when a component is destroyed
- we want to execute some code when a component is updated
- Solution
- we can execute some code when a component is created using
created
(beforeCreate --> created --> beforeMount --> mounted)mounted
(mounted--> beforeUpdate --> updated)updated
(updated --> beforeDestroy --> destroyed)destroyed
(destroyed)
- we can execute some code when a component is created using
<script>
export default {
methods: {
beforeCreate() {
console.log('beforeCreate')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeDestroy() {
console.log('beforeDestroy')
},
destroyed() {
console.log('destroyed')
}
}
}
</script>
- Issue
- We can pass data using props and events but when it comes to passing templates, we can't do that like an ordinary HTML element
- We would like to pass templates to keep our components reusable
- Solution
- We can pass templates using
<slot>
slots<slot name="slot-name">
named slots<slot name="slot-name" v-bind:slot-prop="slotProp">
slots with props
- We can pass templates using
<template>
<div>
<h1>Slots</h1>
<slot></slot>
<slot name="slot-name"></slot>
<slot name="slot-name" v-bind:slot-prop="slotProp"></slot>
</div>
</template>
<script>
export default {
name: 'AppSlots',
props: {
slotProp: {
type: String,
required: true,
default: ''
}
}
}
</script>
// parent component
<template>
<div>
<app-slots>
<h2>Default Slot</h2>
</app-slots>
<app-slots>
<h2 slot="slot-name">Named Slot</h2>
</app-slots>
<app-slots>
<h2 slot="slot-name" slot-prop="Slot Prop">Named Slot with Props</h2>
</app-slots>
</div>
</template>
<script>
import AppSlots from './AppSlots'
export default {
name: 'App',
components: {
'app-slots': AppSlots
}
}
</script>
- Problem
- based on some condition, we want to render a different component in the same place
- when the user clicks on a button, we want to render a different component
- Solution
- we can render a different component using
<template>
<div>
<h1>Dynamic Components</h1>
<button @click="toggleComponent">Toggle Component</button>
<component :is="componentName"></component>
</div>
</template>
<script>
import AppEmployees from './AppEmployees'
import AppSlots from './AppSlots'
export default {
name: 'AppDynamicComponents',
components: {
'app-employees': AppEmployees,
'app-slots': AppSlots
},
data() {
return {
componentName: 'app-employees'
}
},
methods: {
toggleComponent() {
this.componentName = this.componentName === 'app-employees' ? 'app-slots' : 'app-employees'
}
}
}
</script>
- Problem
- when components are destroyed, they are removed from the DOM
- the data is lost
- we would like to keep the data and the DOM
- Solution
- we can keep the data and the DOM using
<keep-alive></keep-alive>
- we can keep the data and the DOM using
<template>
<div>
<h1>Keep Alive</h1>
<button @click="toggleComponent">Toggle Component</button>
<keep-alive>
<component :is="componentName"></component>
</keep-alive>
</div>
</template>
- input modifier
lazy
will update the data only when the input loses focus
<input type="text" v-model.lazy="name">
- Issue
- we want to bind a checkbox to a boolean value
- when a user checks the checkbox, we want to set the value to true and update the data
- Solution
- we can bind a checkbox to a boolean value using
v-model
- we can bind a checkbox to a boolean value using
<input type="checkbox" v-model="checked" value="cb1">
<input type="checkbox" v-model="checked" value="cb2">
<input type="checkbox" v-model="checked" value="cb3">
- Problem
- we want to make HTTP requests to fetch data from a server
- we want to make HTTP requests to send data to a server
- we wamt to be able to make
GET
,POST
,PUT
,PATCH
,DELETE
requests
- Solution
- we can make HTTP requests using
axios
orfetch
orvue-resource
- we can make
GET
,POST
,PUT
,PATCH
,DELETE
requests usingvue-resource
- we can make HTTP requests using
<template>
<div>
<h1>HTTP Requests</h1>
<button @click="getEmployees">Get Employees</button>
<ul>
<li v-for="employee in employees" :key="employee.id">
{{ employee.name }}
</li>
</ul>
</div>
</template>
<script>
import VueResource from 'vue-resource'
export default {
name: 'AppHttp',
data() {
return {
employees: []
}
},
methods: {
getEmployees() {
this.$http.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
this.employees = response.body
})
}
},
created() {
Vue.use(VueResource)
}
}
</script>
methods: {
postEmployee() {
this.$http.post('https://jsonplaceholder.typicode.com/users', {
name: 'John Doe',
email: '[email protected]',
phone: '1234567890'
}).then(response => {
console.log(response)
})
},
putEmployee() {
this.$http.put('https://jsonplaceholder.typicode.com/users/1', {
name: 'John Doe',
}).then(response => {
console.log(response)
})
},
deleteEmployee() {
this.$http.delete('https://jsonplaceholder.typicode.com/users/1').then(response => {
console.log(response)
})
},
patchEmployee() {
this.$http.patch('https://jsonplaceholder.typicode.com/users/1', {
name: 'John Doe',
}).then(response => {
console.log
})
}
}
- Issue
- we would like vue to something for us, this behavior is not provided by vue
- we would want to add a custom behavior to an element
- for example when a user clicks on a button, we want to change the background color of the button randomly
- Solution
- we can add a custom behavior to an element using
directives
- say
v-random-color
is a custom directive that changes the background color of an element randomly
- we can add a custom behavior to an element using
<template>
<div>
<h1>Custom Directives</h1>
<button v-random-color>Random Color</button>
</div>
</template>
<script>
export default {
name: 'AppDirectives',
directives: {
randomColor: {
bind(el, binding, vnode) {
el.style.backgroundColor = '#' + Math.random().toString().slice(2, 8)
}
}
}
}
</script>
- Issue
- we want a custom directive with value isDarkTheme to be true or false
- when the value is true, we want the background color to be black, otherwise we want the background color to be white
- Solution
- we can add a custom directive with arguments using
v-theme-switch:isDarkTheme
- we can access the value of the argument using
binding.value
- we can add a custom directive with arguments using
<template>
<div>
<h1>Custom Directives with Arguments</h1>
<button v-theme-switch:isDarkTheme>Theme Switch</button>
</div>
</template>
<script>
export default {
name: 'AppDirectives',
directives: {
themeSwitch: {
bind(el, binding, vnode) {
if (binding.value) {
el.style.backgroundColor = 'black'
el.style.color = 'white'
this.isDarkTheme = true
console.log('theme', this.isDarkTheme)
} else {
el.style.backgroundColor = 'white'
el.style.color = 'black'
this.isDarkTheme = false
console.log(this.isDarkTheme)
console.log('theme', this.isDarkTheme)
}
}
}
}
}
</script>