vuejs-br / forum

Fórum sobre a tecnologia Vue.js, totalmente em português!
MIT License
222 stars 3 forks source link

Composição de componentes com export de objetos/mixins #27

Open IgorHalfeld opened 5 years ago

IgorHalfeld commented 5 years ago

O @VitorLuizC abriu uma thread falando sobre compor seus componentes usando trechos de outros componentes, já que um componente no Vue é basicamente um objeto, segue o exemplo

Greetings.vue

<template>
  <div class="container">
    <h1>{{ name }}</h1>
  </div>
</template>

<script>
export const greetingsProps = {
  name: { type: String, required: true }
}

export default {
  props: greetingsProps
}
</script>

Header.vue

<template>
  <div class="container">
    {{ name }}
    <Header name="Halfeld">
    {{ age }}
  </div>
</template>

<script>
import Header, {
  greetingsProps
} from '@/components/Greetings'

export default {
  components: { Header },
  props: {
    ...greetingsProps
   age: { type: Number, required: true }
  }
}
</script>

E então, nessa mesma thread o @vinicius73 comentou do uso de mixins e entends que também é uma alternativa quando falamos de composição.

VitorLuizC commented 5 years ago

Acho que é interessante a gente enxergar que antes de ser um componente Vue, o SFC é um módulo JavaScript e tem acesso a todas as ferramentas de granularização/modularização e composição dele. Isso inclui exportar outros valores, funções e partes do componente que podem compor os componentes que o utilizam.

E tenho uma sugestão para não extrapolar o uso dessa técnica. Usem apenas em Composable Components, que são componentes interdependentes e com um mesmo escopo e dependência de UI.

Seguem snippets do uso dessa técnica nos meus componentes FieldContainer e FieldText.

vinicius73 commented 5 years ago

Dei uma modificada para usar mixins e improvisei um componente funcional. Ainda é possível automatizar a geração de outros componentes baseado em tipos de input suportados. Dada a simplicidade dos outros componentes, até seria possível usar 100% de render function, ampliando as possibilidades de customização.

<template>
  <fieldset class="field-container" :class="'field-container-' + id">
    <label v-if="label" class="label" :for="id">{{ label }}</label>
    <slot />
  </fieldset>
</template>

<script>
import generateID from '@/helpers/generateID';

export const FieldContainerProps = {
  id: {
    type: String,
    default: generateID,
  },
  label: String,
};

export default {
  name: 'FieldContainer',
  props: FieldContainerProps,
};
</script>
import FieldContainer, {
  FieldContainerProps,
} from './FieldContainer';

const FieldMixin = {
  components: {
    FieldContainer
  },
  props: {
    ...FieldContainerProps
  }
}

export default FieldMixin
<template>
  <field-container v-bind="{ id, label }" class="field-text">
    <input
      v-bind="{ id, type, value }"
      class="field-input"
      :class="'field-input-' + type"
      v-on="$listeners"
    />
  </field-container>
</template>

<script>
import FieldMixin from './FieldMixin';

export const FieldTextTypes = ['text', 'number', 'password'];

export default {
  name: 'FieldText',
  mixins: [FieldMixin],
  props: {
    type: {
      type: String,
      default: 'text',
      validator: FieldTextTypes.includes.bind(FieldTextTypes),
    },
    value: {
      type: [Number, String],
      required: true,
    },
  },
}
</script>
import { FieldContainerProps } from './FieldContainer'
import FieldText from './FieldText'

export default {
  functional: true,
  props: {
    ...FieldContainerProps,
    value: {
      type: [Number, String],
      required: true,
    },
  },
  render (h, { data, props }) {
    return h(FieldText, {
      ...data,
      props: {
        ...props,
        type: 'password'
      }
    })
  }
}
IgorHalfeld commented 5 years ago

Toppen! Eu renomearia o h para createElement só pra melhorar a legibilidade :D