Skip to content

Web App Meeting (17/04/2023)

Attendees

  • Kiryl ,Jeroen ,Robbe ,Wouter ,Jeffrey ,Tanya ,Nick

Topics

  • AppTeleport
  • Classes
  • Container queries
  • Improving bundle size
  • Security best practices
  • Naming conventions
  • Error handling
  • Caching data

Previous meeting

  • ESLint Config
    • Rules are good, but still needs some work to make it more usable for the formatting

1. AppTeleport

You can create a wrapper around <teleport>. This way you can use it in a component without having to use v-if for the onMounted issue.

vue
<script setup lang="ts">

defineProps<{
  to: string;
}>();

const isMounted = ref<boolean>(false);

onMounted(() => {
  isMounted.value = true;
});
</script>

<template>
  <teleport
      v-if="isMounted"
      :to="to"
    >
    <p>This is Teleported</p>
  </teleport>
</template>

2. Classes

Do classes have a place in Vue? It can be useful for improving and handling business logic.

An example is creating builders for your models. This way you can create a model with a fluent interface.

typescript
class PersonBuilder {
  private person: Person;

  constructor() {
    this.person = {
      firstName: '',
      lastName: '',
      age: 0,
    };
  }

  withFirstName(firstName: string): PersonBuilder {
    this.person.firstName = firstName;
    return this;
  }

  withLastName(lastName: string): PersonBuilder {
    this.person.lastName = lastName;
    return this;
  }

  withAge(age: number): PersonBuilder {
    this.person.age = age;
    return this;
  }

  build(): Person {
    return this.person;
  }
}

const person = new PersonBuilder()
  .withFirstName('John')
  .withLastName('Doe')
  .withAge(30)
  .build();

3. Container queries

Container queries help with responsive design. You can use them to create components that change based on the width of the container instead of the width of the screen.

Live demo in Trixxa.

vue
<script lang="ts" setup>

const props = defineProps({
  cols: {
    type: Number,
    default: 3,
    required: false,
  },
});

const styleClass = computed<string>(() => {
  return new StyleBuilder()
    .add('h-full grid gap-[16px]')
    .addConditional(props.cols > 1, '@md:grid-cols-2')
    .addConditional(props.cols > 2, '@lg:grid-cols-3')
    .addConditional(props.cols > 3, '@xl:grid-cols-4')
    .addConditional(props.cols > 4, '@xxxl:grid-cols-5')
    .addConditional(props.cols > 5, '@xxxxl:grid-cols-6')
    .build();
});
</script>

<template>
  <section class="@container">
    <div :class="styleClass">
      <slot />
    </div>
  </section>
</template>

4. Improving bundle size

General tips:

  • Lazy loading views
  • Lazy loading components
  • Compress images

Interesting links:

Security best practices

https://vuejs.org/guide/best-practices/security.html#reporting-vulnerabilities

5. Naming conventions (Plural or singular)

  • Stores
    • ✅ OfficeStore
    • ❌ OfficesStore
  • Views
    • ✅ OfficeView
    • ❌ OfficesView
  • Detail views
    • ✅ OfficesDetailView
    • ❌ OfficeDetailView
  • Service
    • ✅ OfficesService
    • ❌ OfficeService
  • Module folders
    • ✅ office
    • ❌ offices

6. Error handling

  • What is the best way to handle errors in Vue?
  • What are your best practices?
  • Global error handling on HTTP interceptor?

Example of error handling in a component

typescript

const isLoading = ref<boolean>(false);

const officeStore = useOfficeStore();

try {
  isLoading.value = true;
  officeStore.create(officeForm);
} catch (error) {
  throwErrorToastMessage(error)
} finally {
  isLoading.value = false;
}

HTTP interceptor for handling global errors

typescript
httpClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error?.response?.status === HTTP_STATUS_FORBIDDEN) {
      const { throwToastError } = useToastMessages();
      throwToastError(i18nPlugin.global.t('shared.toast.no_permission'));
      return EMPTY_ERROR;
    }

    if (error?.code === ERR_NETWORK) {
      const { throwToastError } = useToastMessages();
      throwToastError(i18nPlugin.global.t('auth.network_error'));
      return EMPTY_ERROR;
    }

    return Promise.reject(error);
  }
);

7. Caching data

Caching data can be useful for improving performance. You can use it to store data in the browser so you don't have to fetch it again. It avoids unnecessary HTTP calls and makes loading data faster.

Next meeting (24/04/2021)

  • ESlint should be done
  • Presentation about Histoire by Jeffrey
  • ...