User:Saul/vue
This is the page for my notes related to Vue.js
Contents
- 1 Vue
- 1.1 Useful Plugins
- 1.2 Vue 3
- 1.3 Component Properties
- 1.4 CSS
- 1.5 Special Functions / Parameters
- 1.6 html properties
- 2 Vue Router
- 3 Vuex
- 4 Vue Config
- 5 Snippets
Vue
Useful Plugins
vue add router
vue add vuex
vue add eslint
vue add @vuew/pwa
vue add i18n@next # @next has vue 3 support
vue add vuetify # NO vue 3 support at the time of writing.
vue add storybook # NO vue 3 support at the time of writing.
Vue 3
Composition API
https://v3.vuejs.org/api/composition-api.html
Reactive
The reactive function will convert an object into a reactive version.
import { reactive } from "vue";
export default {
setup() {
const book = reactive({ title: "Vue 3 Guide" });
// Any changes to book now will automatically update.
return {book};
}
}
Refs
In the setup method you can make a non-object variable reactive by using the ref function. This variable with become an object with the property value.
Despite it being an object, Vue will still render it as the value not the object (it automatically unwraps it).
import { ref } from "vue";
export default {
setup() {
const readersNumber = ref(0);
console.log(readersNumber.value); // 0
return {readersNumber};
}
}
Render Function
The render function h renders a element with the given state.
Note this can cause problems with state outside of this, for example another component may not get rendered due to not having access to it's state.
import { h } from "vue";
export default {
setup() {
const book = reactive({ title: "Vue 3 Guide" });
return () => h("div", [book.title]);
}
}
Component Properties
data
The data property contains data that your component needs to access. The data property should be a function that returns an object - this so each component has individual data rather than changing all components at once.
export default {
data () {
return {
pageId: this.$router.params.id,
someVarible: "test",
someOtherVarible: 123
};
}
};
props
The props property contains data that is handed down from the parent component or router.
export default {
props: {
someVarible: {
required: true,
type: String,
default: "DEFAULT VALUE",
validator: value => {
// This is for custom validation of the prop
// Return true if the prop is valid
return value.length > 5;
}
}
}
};
methods
The methods property contains functions that modify the state of the component and will have side effects.
export default {
methods: {
someMethod () {
// do something that has some side effects
return "Result";
}
}
};
computed
The computed property contains functions that compute a result but has no other side effects.
export default {
data(){
return {
search: '',
items: words
}
},
computed: {
filteredItems () {
return this.items.filter(item => {
return item.toLowerCase().search(this.search) > -1;
}).sort();
}
}
};
watch
The watch property contains watches - functions that execute when a varible changes.
export default {
data(){
return {
someVarToWatch: "test"
}
},
watch: {
someVarToWatch: (val) => {
Store.dispatch("setSomeVarToWatch", someVarToWatch);
// run some method to update the page to reflect the change
}
}
};
Life Cycle Hooks
Each vue element has a life cycle from before its created to the moment its destroyed, the hooks are listed in order of which they are fired.
beforeCreate
The beforeCreate hook is fired before the vue component is created, you do not have access to the data associated with the component.
export default {
beforeCreate () {
// Do something
}
};
created
The created property is a function that gets executed when the component is created - it is often used to prepare the component. In this hook you have access to the data in the component.
export default {
data () {
return {
image: new Image()
}
},
created () {
this.image.src = "image.jpg";
this.image.onload = () => {
document.getElementById("image").src = this.image.src;
};
}
};
beforeMount
The beforeMount hook is fired before it is mounted or actually altering the DOM.
export default {
beforeMount () {
// Do something
}
};
mounted
The mounted hook is fired when vue mounts the virtual DOM to the real DOM. It is at this moment you have access to this.$el.
export default {
mounted () {
console.log(this.$el.innerText);
}
};
beforeDestroy
The beforeDestroy hook is fired right before the component is destroyed. This hook can be useful for calling cleanup functions etc.
export default {
beforeDestroy () {
// Do something
}
};
destroyed
the destroyed hook is fired after the element is destroyed. This hook can be useful to notify anything else that the component has been destroyed. The element is considered destroyed if it is hidden by a v-if or the vue router etc.
export default {
destroyed () {
// Do something
}
};
components
The components property is a object containing the components your component uses like the example below:
<template>
<importedComponent></importedComponent>
</template>
<script>
import importedComponent from "./importedComponent";
export default {
components: {
importedComponent
}
};
</script>
mixins
The mixins property is useful for providing the same functionality across multiple components, like showing a loading symbol while that data is fetch from a database.
Mixins themselves have the same properties as components - this is because when you add a mixin to your component you copy all the mixin data to it.
Mixins are usually kept in a separate folder called "mixins" with each mixin in its own file. It is preferable to prefix the properties in the mixin with FILENAME_ to avoid collisions and help with locating what mixin does what.
Here is an example showing simple usage:
// src/mixins/asyncDataStatus.js
export default {
data () {
return {
asyncDataStatus_ready: false
};
},
methods: {
asyncDataStatus_fetched () {
this.asyncDataStatus_ready = true;
}
}
};
<!-- src/pages/PageHome.vue -->
<template>
<div v-if="asyncDataStatus_ready">
Fetched data: {{ theData }}
</div>
<div v-else>
Loading
</div>
</template>
<script>
import asyncDataStatus from "@/mixins/asyncDataStatus";
export default {
mixins: [asyncDataStatus],
data () {
return {
theData: ""
};
},
created () {
fetch('http://example.com/movies.text').then(response => {
this.theData = response;
this.asyncDataStatus_fetched();
});
}
};
</script>
filters
The filters property are used to modify a variable type into a specific format for example timestamps to dates, currencies ect.
Filters are just methods with special syntax in the template section.
<template>
<div>
{{ timestamp | humanFriendlyDate }}
</div>
</template>
<script>
import moment from "moment";
export default {
data () {
return {
timestamp: 1509638478
}
},
filters: {
humanFriendlyDate (date) {
return moment.unix(date).format("MMMM Do YYYY, h:mm:ss a");
}
}
}
</script>
template
The template property can be used to inject code into an vue instance.
new Vue({
el: "#app",
template: "<App/>",
components: { App }
});
render
The render property appears to be some black magic to somehow specify what to render in that element.
The most common use case is in main.js to render the application:
import Vue from "vue";
import App from "./App";
/* eslint-disable no-new */
new Vue({
el: "#app",
render: h => h(App)
});
router
The router property defines the router that the vue component will use.
The navigation guards are hooks that call when the route changes. They each have three parameters: to, from, and next.
beforeRouteEnter
This hook triggers just before the route enters the path with the component in it.
beforeRouteEnter (to, from, next) {
}
beforeRouteUpdate
This hook triggers when the the route changes but still has this component in it.
beforeRouteUpdate (to, from, next) {
}
beforeRouteLeave
This hook triggers just before the route leaves the path with the component in it. A useful case for this hook is to verify they want to leave a form page before they lose their data.
beforeRouteLeave (to, from, next) {
const confirmed = window.confirm("Are you sure you wish to leave?");
confirmed ? next() : next(false);
}
store
The store property defines the store that the vue component will use.
CSS
Scoped
The style section can have the optional parameter "scoped" which makes the css only local to the component it is attached to instead of being global. You may add 2 style tags to your component if you wish to have both local and global css.
<style>
/* global css code */
</style>
<style scoped>
/* local css code */
</style>
Modules
The style section can have the optional parameter "module" which seperates the css from the rest. You have to access the class selectors with the v-bind class parameter likes so:
<template>
<div>
<h1 :class="$style.test">Test 1</h1>
<h1 :class="$style['test-two']">Test 2</h1>
</div>
</template>
<style module>
.test {
color: red;
}
.test-two {
color: blue;
}
</style>
Special Functions / Parameters
set
Vue cannot detect changes in objects so to manually tell vue to update the data you can use the vue.set() function as shown below:
var object = {
propertyName: "initialValue"
};
var value = "someValue";
Vue.set(object, "propertyName", value);
To use Vue.set you need to import Vue in the file - if you don't want to do this you can use this.$set instead like so:
this.$set(object, "propertyName", value);
emit
To make your component emit a custom event you can use this.$emit like so:
export default {
methods: {
emitEvent () {
this.$emit("emittedEvent", {someData: "data"});
}
}
}
Then you can listen to the event from other components using v-on like demonstrated below:
<template>
<div>
<importedComponent @emittedEvent="doSomething"></importedComponent>
</div>
</template>
<script>
import importedComponent from "@/components/importedComponent";
export default {
components: {
importedComponent
},
methods: {
doSomething () {
console.log("done something");
}
}
}
</script>
root
Vue components and subcomponents can access the root vue instance that created them and access all the properties of it including being able to call its methods, computed properties etc.
this.$root.someMethod();
Global Properties
You can register properties globally by using Vue.property
It is usually preferable to do this in the main.js file so that it gets registered once.
Here is an example registering a component globally:
The component property is used to register components globally allowing you to use them without importing them in every component that needs it.
import importedComponent from "@/components/importedComponent";
Vue.component("componentName", importedComponent);
html properties
slots
Creating
Slots are a way of inject data into a component on a case by case basis.
Your components will have somewhere in it:
<slot><!-- some default code can be put in between and will get used if the slot doesn't get used --></slot>
You can also have multiple slots and specify them by the name attribute:
<slot name="title"></slot>
Using
You can use the slots when you use your component like this:
<YourComponent>
<span slot="title">This will be injected into the slot with the name="title" attribute.</span>
<p>This will be injected into the slot with no name attribute.</p>
</YourComponent>
ref
ref is used to specify a reference to something you want to access where the component is not yet created or after its been destroyed and this is undefined.
<template>
<div ref="someReference"></div>
</template>
<script>
export default {
beforeRouteLeave (to, from, next) {
console.log(this.$refs.someReference);
}
}
</script>
.prevent
.prevent is used as short-hand to prevent default behavior, the following example shows it preventing from submission:
<form @submit.prevent="someMethod">
.stop
.stop is used to stop event propagation.
<div @click.stop="someMethod"></div>
once
.once
.once makes the event as the name suggest happen only once.
<div @click.once="someMethod"></div>
v-once
v-once can be added to the element so that everything in it only runs once.
<div v-once>{{ someData }}</div>
someData will remain its initial value even after it has changed.
.exact
.exact makes the event trigger only if it happens with no other feedback, for example:
<div @click.exact="someMethod"></div>
The event will only fire when it is clicked, it will not fire if another mouse or keyboard button is down when it is clicked.
.self
.self makes the event only trigger if it happens to the element itself - not child elements.
<div @click="someMethod">Will trigger<div>Will trigger.</div></div>
<div @click.self="someMethod">Will trigger<div>Won't trigger.</div></div>
.number
.number makes the input only accept numbers, it ignores any whitespace in the beginning.
<input v-model.number="data" />
<!--
Input: Output:
"123" 123
" 123" 123
"123a123" 123
"a123a123" "a123a123"
-->
.trim
.trim filters out unnecessary whitespace, for example:
<input v-model.trim="data" />
If you enter " 1 2 3 4 ", the data will be stored as: "1 2 3 4" - thus cutting out consecutive whitespace and whitespace in the beginning and end of the string.
.lazy
.lazy makes the event only happen once it loses focus, for example:
<textarea v-model.lazy="data" />
The data will only update when the user clicks away.
.capture
.capture priorities the event trigger, for example:
<div @click="someMethod">
<div @click="someOtherMethod">CLICK</div>
</div>
When "CLICK" is clicked someOtherMethod will be triggered then someMethod.
<div @click.capture="someMethod">
<div @click="someOtherMethod">CLICK</div>
</div>
Here, when "CLICK" is clicked someMethod will be triggered before someOtherMethod.
.passive
.passive makes the event happen as it is triggering instead of after is is fired, for example:
<div v-on:scroll.passive="someMethod"></div>
The event will fire when the div is scrolling in contrast to when it has finished scrolling.
NOTE: do not use .passive and .prevent together.
v-model
v-model is shorthand for :value and @input the two examples below do the same thing:
<input type="text" v-model="someTextVar" />
<input type="text" :value="someTextVar" @input="someTextVar = $event.target.value" />
:class
- class is a way for vue to create dynamic classes, there are two syntaxes for this object and array.
<div :class="{className: vueDataVarible}"></div> <!-- The div has the class "className" when vue's data property "vueDataVarible" is set to true -->
<div :class="[vueDataVarible ? 'classNameOne' : 'classNameTwo']"></div> <!-- The div will toggle between "classNameOne" and "classNameTwo" depending on the state of vueDataVarible -->
<div :class="[vueDataVarible ? 'className' : '']"></div> <!-- The div will toggle the class "className" depending on the state of vueDataVarible -->
Keyboard Events
Keyboard events can be triggered using @keydown.key or @keyup.keyCode, here are a few examples
<input type="text" @keyup.enter="someMethod" /> <!-- Triggers when the "enter" key is released -->
<input type="text" @keyup.k="someMethod" /> <!-- Triggers when the "k" key is released -->
<input type="text" @keydown.enter="someMethod" /> <!-- Triggers when the "enter" key is pushed -->
<!--
NOTE: this will trigger once then many more times due to key repeats if it is held
for example if you hold the "z" key for 3 seconds you would end up with 15 "z"'s and an event triggered for each one.
-->
<input type="text" @keydown.ctrl.c="someMethod" /> <!-- Triggers when the "ctrl-c" combination is pushed -->
<input type="text" @keydown.shift.c="someMethod" /> <!-- Triggers when the "shift-c" combination is pushed -->
<input type="text" @keydown.alt.c="someMethod" /> <!-- Triggers when the "alt-c" combination is pushed -->
<input type="text" @keydown.k.c="someMethod" /> <!-- Triggers when EITHER "k" or "c" is pushed -->
<!--
Other key modifiers:
.tab
.delete
.esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
-->
Mouse Events
Mouse events can be triggered in similar fashion to keyboard events, here are a few examples:
<input type="text" @mouseup.left="someMethod" /> <!-- Triggers when the left mouse click is released -->
<input type="text" @mousedown.right="someMethod" /> <!-- Triggers when the right mouse click is pushed -->
<input type="text" @mousedown.middle="someMethod" /> <!-- Triggers when the middle mouse click is pushed -->
<textarea @scroll="someMethod" /> <!-- Triggers when the text area is scrolled -->
<!--
NOTE: the event will be triggered event if the textarea is scrolled by input overflow.
-->
<div @mouseover="someMethod"></div> <!-- Triggers once the mouse enters the elements area -->
<div @mouseout="someMethod"></div> <!-- Triggers once the mouse leaves the elements area -->
<div @click="someMethod"></div> <!-- Triggers when that element is clicked -->
<div @dblclick="someMethod"></div> <!-- Triggers when that element is double clicked -->
<div @click.ctrl="someMethod"></div> <!-- Triggers when that element is clicked while holding the "ctrl" key -->
Vue Router
Router Links
Router links can be used much like the usual a elements:
<a href="/somePage">link</a>
<router-link :to="/somePage">link</router-link>
Or you can specify the router route name and params like so:
<router-link :to="{name: 'Page', params: { id: 123 }}">
link
</router-link>
Where router routes contains something like:
{
path: "/page/:id",
name: "Page",
component: componentName,
props: true
}
Nested Routes
You can define nested routes using the children attribute:
{
path: "/page",
name: "Page",
component: Page,
children: [
{
path: "somePage",
name: "somePage",
component: SomePage,
}
]
}
The route /page will render Page.
The route /page/somePage will render Page and if Page contains a router-view component it will also display SomePage.
History Mode
To set the router to html5 history mode you need to specify the mode parameter in your router.js file like so:
export default new Router({
routes: [
{
path: "/",
name: "Home",
component: Home
}
],
mode: "history"
});
Router Guards
Router guards are hooks the call when the route changes allowing you to check for things like if a user is signed in an conditionally redirect.
beforeEnter
The beforeEnter hook fires right before the route has entered that path.
path: "/profile",
name: "Profile",
beforeEnter (to, from, next) {
if (store.authUser) {
next();
} else {
next(false);
}
}
beforeLeave
The beforeLeave hook fires right before the route leaves that path.
path: "/profile",
name: "Profile",
beforeleave (to, from, next) {
}
Router Methods
You can access the vue router in a component like this:
this.$router
push
The router has a push method so you can change the route when an event happens.
this.$router.push({name: "ROUTE", params: {}});
beforeEach
You can define global navigation hooks using the beforeEach method:
router.beforeEach((to, from, next) => {
document.title = to.name;
next();
});
afterEach
You can also define global navigation hooks using the afterEach method:
router.afterEach((to, from) => {
document.title = to.name;
});
Custom Route Meta
You can define custom route meta using the meta parameter like this:
{
path: "/",
name: "Home",
component: Home,
meta: { requiresAuth: true }
}
Then you can access the meta in navigation guards or components: router.beforeEach((to, from, next) => { console.log(to.meta.requiresAuth); }); </source>
Vuex
Special Functions
Store
The store function is used to create a vuex store like the below example.
new Vuex.Store({
state: {
},
getters: {
},
actions: {
},
mutations: {
}
});
Store Properties
State
The state property is used to hold all the varibles need, and is similar to vue's data property.
new Vuex.Store({
state: {
storedDataOne: "someData",
storedDataTwo: 123,
storedDataThree: [],
storedDataObject: {
objectPropertyOne: []
}
}
});
Getters
The getters property is used to return the data in the format needed, much like vue's computed property.
getters: {
storedDataThreeCount () {
return state.storedDataThree.length;
}
}
Actions
The actions property is used to do more complex actions like fetch data with an ajax call and then call a mutation to update the data, actions can be likened to vue's methods property.
mutations: {
fetchStoredDataTwo (context) {
// Fetch data from server
// context.commit(data);
}
}
To call an action you can use the dispatch method:
store.dispatch("fetchStoredDataTwo");
Mutations
The mutations property is used to do simple changes to the store state.
mutations: {
setStoredDataTwo (state, someNewData) {
state.storedDataTwo = someNewData;
}
}
To run a mutation in a component you first must import the store then commit the state:
store.commit("setStoredDataTwo", 321);
Map Helpers
The map functions are helper functions for access the vuex store - they can significantly reduce your code if you have a lot of computed properties, methods or anything else that only makes a call to the store.
Examples
Here is an example where this code can be reduced significantly:
computed: {
products () {
return this.$store.state.products;
},
cart () {
return this.$store.state.cart;
},
productIsInStock () {
return this.$store.getters.productIsInStock;
}
message () {
return this.$store.getters.message;
}
},
methods: {
checkout: {
$store.dispatch('checkout')
}
}
import {mapState, mapGetters} from "vuex";
// ...
computed: mapState({
products: state => state.products,
cart: "cart" // OR: state => state.cart
}),
methods: mapActions(["checkout"]);
/* OR:
methods: mapActions({
checkout: "checkout"
})
*/
You may change the names of theses properties as well:
import {mapState, mapGetters} from "vuex";
// ...
computed: mapState({
allProducts: state => state.products,
shoppingCart: state => state.cart
})
To copy these functions to the computed property rather than set the computed property to them you can use the spread operator (...)
Final Example
import {mapState, mapGetters, mapActions} from "vuex";
// ...
computed: {
...mapState({
allProducts: "products" // OR: state => state.products,
shoppingCart: "cart" // OR: state => state.cart
}),
...mapGetters(["productIsInStock", "message"])
},
methods: {
...mapActions(["checkout"])
}
If you compare this to the code not using it you can see that you save a lot of room and simplify code.
List of map helpers
There is a map helper for each vuex store property:
- mapstate
- mapGetters
- mapActions
- mapMutators
Modules
To split a vuex store file up you could import actions from one file getters from another and so on or you can use vuex's module system which is a way to split state up into separate items. You might have a module for authentication another one for your product list, another for your cart and so on.
Module Files
You usually have you store structure setup like this for modules:
store/
modules/
auth.js
cart.js
products.js
index.js
Each module file contains mostly the same code as a store file:
export default {
state: {
},
getters: {
},
actions: {
},
mutations: {
}
}
Each module should only contain the code related to itself.
Imported Modules
To import vuex modules you can use the modules property like so:
import Vuex from "vuex";
import Vue from "vue";
import cart from "./modules/cart";
import products from "./modules/products";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
auth,
cart,
products
},
state: {
},
getters: {
},
actions: {
},
mutations: {
}
});
The store file should contain the state, actions, etc that uses multiple modules or fit better in a global scope.
Module's Scope
The actions, mutators, and getters in each module are defined in a global scope therefore no change is needed in the components code.
The state property in each module is local so you must define the module who's state you wish to access for example:
computed: {
...mapState({
products: state => state.products.items // INSTEAD OF: state.items
}),
}
The Root State
If you need to access the store file's state from a module or another module you can use the rootState parameter. The getters have the rootState as its third parameter.
getters: {
stateExample (state, getters, rootState) {
// Access the store file's state
console.log(rootState) // This will output the state object in the stores index.js file
console.log(rootState.someVar) // This will output whatever someVar is set to in the stores index.js file
// Accessing other modules state
console.log(rootState.someModule) // This will output the state object in the module "someModule"
console.log(rootState.someModule.someVar) // This will output whatever someVar is set to in the "someModule" module
}
}
Namespaces
One of the problems with modules is that the getters, actions, and mutators are global - therefore when two or more modules have the same action and a component calls that action all of the modules with that action will have that action executed - possibly helpful in some rare cases, or more likely create bugs.
Namespaces are a way to further separate modules - so that the modules have their own local getters, actions, and mutators.
Namespacing Modules
To define a namespace you can set the namespaced property to true like so:
export default {
namespaced: true,
state: {
// ...
}
// ...
}
The Root Getters
The root getters are accessed the same way as the root state. In the getters the "rootGetters" parameter is passed as the fourth parameter.
Here is an example of usage:
getters: {
cartProducts (state, getters, rootState, rootGetters) {
rootGetters["products/productIsInStock"];
}
},
actions: {
addProductToCart ({commit, state, getters, rootState, rootGetters}, product) {
rootGetters["products/productIsInStock"](product);
}
}
Commiting Actions To Other Modules
To commit an action to another module that is a child module of the module or store that is trying to commit the action like this:
commit("products/decrementProductInventory", product);
To commit an action to another module that is not the child module of the module that is trying to commit it you can define the call from root like this:
commit("products/decrementProductInventory", product, {root: true});
Access Namespaced Modules From Components
The components need to specify the namespace's module who's action is getting called. This can be done in two ways as these examples demonstrate for a namespaced "cart" module:
computed: {
...mapGetters({
products: "cart/cartProducts",
total: "cart/cartTotal",
productIsInStock: "products/productIsInStock"
}),
...mapState({
checkoutStatus: state => state.cart.checkoutStatus
})
}
computed: {
...mapGetters("cart", {
products: "cartProducts",
total: "cartTotal"
}),
...mapGetters("products", {
productIsInStock: "productIsInStock"
}),
...mapState("cart", {
checkoutStatus: state => state.checkoutStatus
})
}
Unique Module Instances
Sometimes it can be useful to have unique modules with multiple instances.
To do so it can be helpful to encase your module in a function that returns the Vuex store object like so:
export default () => ({
namespaced: true;
state: {}
// ...
});
Then to instantiate it just import it and call the module as a function when you specify a new Vuex store like so:
import myUniqueModle from "./my-unique-module";
// ...
state.thisModulesUniqueSubModule = new Vuex.Store(myUniqueModle());
Global Store
If you would like store to be accessable global you can define it in the root vue component (usually the main.js file).
import Vue from "vue";
import App from "./App";
import store from "@/store/index";
new Vue({
el: "#app",
store,
render: h => h(App)
});
Then you can access the store from all components without importing it like so:
this.$store
Vue Config
Aliases
By default vue has an alias "@" to reference the src folder in the project
import Home from "@/pages/home";
You can add your own custom aliases by editing the build/webpack.base.config.js file like so:
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
'pages': resolve('src/pages')
}
},
and you can use it like so:
import Home from "pages/home";
Snippets
Handling Window Resize
export default {
methods: {
handleResize (e) {
// Do something
}
},
mounted () {
this.$nextTick(() => {
window.addEventListener("resize", this.handleResize);
});
},
beforeDestroy () {
window.removeEventListener("resize", this.handleResize);
}
}