A Point of Vue
Tips and Tricks for your Vue.js Application
Agenda
- Calling Method in Templates
- Initial Load for Interpolation
- Passing Attributes Along
- MapState not Reactive
- Difference between Props and Data
Calling Method in Templates
It is quite common to execute a method within a template. One common use case would be to format price or date.
Tell me, why?
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>
Initial Load for Interpolation
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.
Hey, you've got to hide your love away
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;
}
Passing Attributes Along
There are cases where we need to create a wrapper for a library component or extend a library component with further behaviour.
Faces pass and I'm home bound
$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'
});
MapState not Reactive
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.
I'm in the middle of a chain reaction
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}`;
}
}
});
Difference between Props and Data
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.
Where it's due, give props to you know who
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>
Resources
- Keystone
-
Thief
- Thief Vue Components: https://github.factset.com/FactSet/thief/tree/master/packages/vue
- Analytics Reporting
Don't make everything Reactive
If you have a non-reactive variable do not define it under data as this will create unnecessary watchers.
We change the World, You and Me
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';
},
},
})
Single Root Components
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.
Don't wait or say a Single Vow
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'},
],
};
},
});
}
A Point of Vue
By Thomas Truong
A Point of Vue
- 67