


/me
William Correa
@wilcorrea




-> Agenda
- Gestão de estados
- Aplicando extends e mixin
- Segredos do watch
- Templates limpos

Aviso:
Este material contém opiniões pessoais

->Gestão de estados
- $emit & props
- data & computed
- $options
- Vuex
- Event Bus
-> $emit & props
- $attrs vs $props
- sync & update
- $emit & on

-> $emit & props

- $attrs vs $props
-> $emit & props

- sync & update
-> $emit & props
- $emit & on

-> data & computed
- data: propriedades de primeira classe

-> data & computed
- computed: estados derivados

-> data & computed
- computed: estados derivados

-> data & computed
- computed: estados derivados

-> data & computed
- computed: estados derivados

-> data & computed
- computed: cache e reatividade
computed: {
now: function () {
return Date.now()
}
}
computed: {
now: function () {
return this.ticker > 0 ? Date.now() : ''
}
}
-> $options
- referência para as opções do construtor

// analog to
new Vue({ ... })
-> $options

-> Vuex
- Variável global gourmetizada
- Sessão
- Reatividade

-> Vuex
- Observables
- Pub/Sub

-> Vuex

-> Vuex

-> $emit & prop vs vuex

- Propagação de estados com $emit & prop
-> $emit & prop vs vuex

- Propagação de estados com vuex
-> Event Bus
- Usar um mesmo componente
- Manipulação de escopo

import Vue from 'vue';
export const EventBus = new Vue();
// Import the EventBus we just created.
import { EventBus } from './event-bus.js';
// emitter
EventBus.$emit('drawer::setState', true)
// receiver
EventBus.$on('drawer::setState', (state) => {
if (state) {
this.openDrawer()
return
}
this.closeDrawer()
})
// receiver
EventBus.$off('drawer::setState')
-> Aplicando extends e mixin
- Extends
- Mixin
- Design Patterns

-> Extends
<template>
<button @click="click">Click me</button>
</template>
<script>
export default {
methods: {
click () {
window.alert('Clica ni mim!')
}
}
}
</script>
import AppButton from './AppButton'
export default {
extends: AppButton,
methods: {
click() {
window.alert('Clica aqui no pai!')
}
}
}
-> Extends


-> Mixin

export default {
methods: {
send() {
window.alert('O pai resolveu já!')
}
}
}
-> Design Pattern
- Chain Of Responsibilities

<template>
<button @click="click">Click me</button>
</template>
<script>
export default {
methods: {
click () {
// use here to check in runtinme
this.checkHandler()
this.handler()
},
checkHandler () {
if (this.handler) {
return
}
throw new Error('The "handler" method is required')
}
},
created () {
// check on create component
this.checkHandler()
}
}
</script>
-> Design Pattern
- Chain Of Responsibilities
import AppButton from "./AppButton";
export default {
extends: AppButton,
methods: {
handler() {
window.alert("Ai sim ein?!");
}
}
};
-> Design Pattern
- Strategy
<template>
<button @click="click">Click me</button>
</template>
<script>
export default {
$handler: undefined,
methods: {
click() {
this.$options.$handler.execute();
},
checkHandler() {
if (this.$options.$handler) {
return;
}
throw new Error('The "handler" method is required');
}
},
created() {
this.checkHandler();
}
};
</script>
-> Design Pattern
- Strategy
import AppButton from "./AppButton";
export default {
extends: AppButton,
$handler: {
execute() {
window.alert("Ai sim ein?!");
}
}
};
-> Segredos do watch
- $watch
- immediate
- deep
- Notação usando . (dot notation)

-> Segredos do watch
- $watch
<template>
...
</template>
<script>
export default {
created () {
if (!this.prop) {
return
}
this.unWatch = this.$watch(this.prop, () => this.$emit('it-works'))
}
}
</script>
-> Segredos do watch
- immediate
- deep
<template>
...
</template>
<script>
export default {
watch: {
prop: {
immediate: true,
handler () {
this.$emit('it-works')
}
}
}
}
</script>
<script>
export default {
watch: {
prop: {
deep: true,
handler () {
this.$emit('it-works')
}
}
}
}
</script>
-> Segredos do watch
- Notação usando . (dot notation)
<script>
export default {
watch: {
'$router.fullPath' () {
this.$emit('crazy-day')
}
},
created () {
this.unWatch = this.$watch('orderItem.product.price', ...)
}
}
</script>
-> Templates limpos
- componentes de uma linha
- peças menores compõe maiores
- estados organizados
- eventos bem alinhados
- proxy components podem ajudar
-> Componentes especialistas
<form class="grid">
<AppInput
class="width-6"
label="Name"
v-model="record.name"
/>
<AppInput
class="width-6"
label="Last Name"
v-model="record.lastName"
/>
<AppSelect
class="width-12"
label="Estado"
v-model="record.state"
v-bind="schema.state"
@input="onInputState"
/>
<AppSelect
class="width-12"
label="Cidade"
v-model="record.city"
v-bind="schema.city"
/>
</form>
<template>
<div class="cell">
<label>{{ label }}</label>
<input v-bind="bind" :value="value" @input="input">
</div>
</template>
<script>
export default {
props: ["label", "value"],
computed: {
bind() {
return { ...this.$props, ...this.$attrs };
}
},
methods: {
input($event) {
this.$emit("input", $event.target.value);
}
}
};
</script>
-> Componentes compostos
export default {
name: "AppField",
props: ['label', 'value'],
computed: {
bind() {
return { ...this.$props, ...this.$attrs }
}
},
methods: {
input($event) {
this.$emit('input', this.parseValue($event))
},
parseValue($event) {
return $event.target.value
}
}
}
<template>
<div class="cell">
<label>{{ label }}</label>
<input
v-bind="bind"
:value="value"
@input="input"
/>
</div>
</template>
<script>
import AppField from "./AppField";
export default {
extends: AppField,
name: "AppInput"
};
</script>
<template>
<div class="cell">
<label>{{ label }}</label>
<select v-bind="bind" v-model="model" @input="input">
<option
v-for="(option, index) in options"
:value="option[optionValue]"
>
{{ option[optionLabel] }}
</option>
</select>
</div>
</template>
<script>
import AppField from "./AppField";
export default {
extends: AppField,
methods: {
parseValue($event) {
return this.getOption($event.target.value);
},
// ...
},
watch: {
value: {
immediate: true,
handler(value) {
this.model = value[this.optionValue];
}
}
}
};
</script>
-> Extender componentes
import AppForm from "./AppForm";
import { getStates, getPerson, getCities } from "../services";
export default {
extends: AppForm,
name: "MyForm",
record: { name: undefined, lastName: undefined, state: undefined, city: undefined },
schema: { ... },
computed: {
recordComputed() {
return {
fullName: `${this.record.name} ${this.record.lastName}`
};
}
},
methods: {
async initialize() {
this.schema.state.options = await getStates();
this.record = await getPerson();
}
},
watch: {
async "record.state"(value) {
this.schema.city.options = await getCities(value);
}
}
};
-> Componentes reusáveis
import AppInput from './AppInput'
import AppSelect from './AppSelect'
export default {
name: 'AppForm',
components: { AppInput, AppSelect },
data: () => ({ record: this.$options.record, schema: this.$options.schema }),
computed: {
payload() {
return { ...this.record, ...this.recordComputed }
},
recordComputed() {
return {}
}
},
methods: {
initialize() {}
},
created() {
this.initialize()
}
}
-> Mixins para operações
<template>
<div class="AppForm">
<form class="grid">
<!-- ... -->
<div class="cell buttons">
<button @click="save">Save Me</button>
</div>
</form>
<pre>{{ payload }}</pre>
</div>
</template>
<script>
import AppForm from "./AppForm";
import MixinOperations from "./MixinOperations";
import { getStates, getPerson, getCities } from "../services";
export default {
extends: AppForm,
mixins: [MixinOperations],
name: "MyForm",
// ...
};
</script>
hora das palmas
Links
Dicas e truques para criar códigos manuteníveis usando Vue.js
By William Correa
Dicas e truques para criar códigos manuteníveis usando Vue.js
- 1,200