Tips and Tricks for your Vue.js Application
It is quite common to execute a method within a template. One common use case would be to format price or date.
A method invocation will ALWAYS run the function whenever a re-render happens.
Computed properties are cached based on their reactive dependencies. This means it will only re-evaluate when some of its reactive dependencies have changed.
new Vue({
el: "#app",
data: {
counter: 0,
date: "2019-05-28",
},
computed: {
getDate: function(){
alert("Happy Birthday Thomas");
return new Date(this.date);
}
}
})
<div id="app">
<div> My Birthday: {{ getDate }}<div>
<div>{{ counter }}</div>
<button @click="counter++">Increase Counter</button>
</div>
Another common behaviour we notice when using binding frameworks such as Angular or Vue.js is that we might notice that initial flicker or appearance of braces.
The string interpolation syntax appears on the page until Vue.js is bootstrapped. Since the syntax is part of the HTML as normal text, the browser will render it as such.
The directive v-cloak will remain there until the Vue instance associated with the template finishes. Therefore, we can add a CSS style which ensures that all elements with the v-cloak directive are not displayed.
<div id="app">
<p><b>Who are you?!</b></p>
<p>{{ response }}</p>
</div>
[v-cloak] {
display: none;
}
There are cases where we need to create a wrapper for a library component or extend a library component with further behaviour.
$attrs contains parent-scope attributes except for class and style. It can be passed down to inner children component using v-bind.
By default parent-scope attribute bindings will fall-through and be applied to the root element of the child component. By setting inheritAttrs to false this behaviour can be disabled.
<template id="my-alert-template">
<div>
<b-button @click="toggle">
{{ show ? '再见' : '你好' }}
</b-button>
<b-alert v-bind="attrs" v-model="show" class="mt-3">
It means "Hello". Now Bye!.
</b-alert>
</div>
</template>
Vue.component('my-alert', {
inheritAttrs: false,
data() {
return {
show: false
}
},
methods: {
toggle() {
this.show = !this.show
},
},
computed: {
attrs() {
const {blah, ...attrs} = this.$attrs;
return attrs;
},
},
template: '#my-alert-template'
});
Vuex provides a single place to keep your application data or simply called state. All of your components get access to your Vuex state, which is reactive.
However, a common problem you may run into is your mapState not being reactive.
Due to the limitations of JavaScript. Vue cannot detect property addition or deletion. Vue performs the getter/setter conversation process during instance initialization.
Therefore, data must be present in order for Vue to convert it to make it reactive.
<div id="app">
<p>{{ message }}</p>
<p>{{ count }}</p>
<button @click="increment">+</button>
</div>
const store = new Vuex.Store({
state: {
count: 0,
message: '',
},
actions: {
increment ({ commit }) {
commit('increment');
}
},
mutations: {
increment (state) {
state.count++;
state.message = `Counting ${state.count}`;
}
}
});
Mutating a prop locally is considered an anti-pattern. Due to the rendering mechanism, whenever the parent component re-renders, the child component's local changes will be overwritten.
We need a computed property or additional data to store the prop value.
In some cases, we may need two-way binding for a prop, therefore, emitting events in the pattern of `update:propName` is the recommend way.
We can then listen to that event and update a local data property. However, the `.sync` modifier exist to combine the pattern together for convenience.
Vue.component('PokeBall', {
template: `<div>Poke Ball: {{pokemon}}<br><butto...`,
props: ['pokemon'],
data() {
return {
chosen: '',
}
},
mounted() {},
methods: {
changePokemon() {
this.$emit('update:pokemon', 'Bulbasaur');
},
},
});
<div id="app">
Starter: {{ starter }}
<poke-ball :pokemon.sync="starter"></poke-ball>
</div>
If you have a non-reactive variable do not define it under data as this will create unnecessary watchers.
By avoiding data to create the constant we can still attach it to the this context by initialising under created hook.
new Vue({
el: "#app",
data: {
},
created() {
this.identity = 'Peter Parker';
},
methods: {
change() {
alert('Spidey Sense');
this.identity = 'Tom Holland';
},
},
})
Component template should contain exactly one root element. Most solution involves wrapping your content in a div.
Unfortunately it does not always work. Someone you may require to return more than one, such as list.
The way around this problem is using functional components, where the render function with produce the relevant template.
However, this comes with a price, as functional components don't have any lifecycle hooks passed to it, the are instance-less as well you can not refer to the `this` context.
Vue.component('Korean', {
props: ['words'],
functional: true,
render(createElement, { props }) {
return props.words.map(word =>
createElement('li', `${word.kor} - ${word.eng}`)
);
},
});
window.onload = () => {
new Vue({
el: '#app',
data() {
return {
words: [
{kor: '안녕하세요', eng: 'Hello'},
{kor: '밥 먹었어요', eng: 'Have you eaten?'},
{kor: '반갑습니다', eng: 'Nice to meet you'},
{kor: '감사합니다', eng: 'Thank You'},
],
};
},
});
}