/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,185