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.
<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.
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.
<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:
- https://www.npmjs.com/package/vite-plugin-compression
- https://www.npmjs.com/package/rollup-plugin-visualizer
- https://vitejs.dev/guide/build.html#chunking-strategy
- https://kazupon.github.io/vue-i18n/guide/lazy-loading.html
Security best practices
https://vuejs.org/guide/best-practices/security.html#reporting-vulnerabilities
- https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
- https://html5sec.org/
- Http security headers
- CSP (Content Security Policy)
- XSS (Cross-site scripting)
- CSRF (Cross-site request forgery)
- CORS (Cross-origin resource sharing)
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
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
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.
- Axios Cache Interceptor: https://axios-cache-interceptor.js.org/
- Package for caching HTTP calls in Axios
- Interesting article about caching data: https://arthur.place/implications-of-cache-or-state
- Talks about the difference between caching data and storing data in state
Next meeting (24/04/2021)
- ESlint should be done
- Presentation about Histoire by Jeffrey
- ...