Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add docs 'sending only necessary data to client' #213

Merged
merged 7 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions docs/performance/sending-only-necessary-data-to-client/en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
description: Sending only necessary data to client
since: 1.0.0
---

When loading data from external APIs using [Loaders](/docs/en/concepts/loader) and sending them to the [Section](/docs/en/concepts/section), it's possible that the size of the payload may negatively impact the site's performance. The impact occurs both in the initial loading time and in the [hydration](https://blog.saeloun.com/2021/12/16/hydration/), where the page is "initialized" in the browser to make it interactive (using `useEffect`, `useSignal`, etc.). It's possible to visualize the size of the final JSON through the **Performance** tab in the CMS deco.

![288067513-db3a14e1-c0ac-47f8-83b9-afc8db60de71](https://github.com/deco-sites/starting/assets/76822093/ec005f5d-4169-4e89-acd0-8c06baf3c80d)

When the JSON size exceeds ~500kb, it's likely that the UI doesn't need the complete data but rather some part of it (or a computation based on other values). To reduce this size and improve page performance, it's possible to **filter the data** still on the loader to ensure that only the necessary data is passed to the UI.


## Example Code - 1

In this first example, we will show how to avoid sending too much data to an Island.

Let's say there's a component called ProductCard, which receives the entire JSON of a product.

```tsx
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){
return(
<div>
<Image src={product.image} width="100" height="100"/>
</div>
)
}
```

In it, you want to include an [Island](https://fresh.deno.dev/docs/concepts/islands) to create the buy button.

```tsx
import BuyButton from "$store/components/ui"
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){

return(
<div>
<Image src={product.image} width="100" height="100"/>
<BuyButton />
</div>
)
}
```

It's possible that this BuyButton may need some product information to be able to add it to the cart.

Here is where we should be careful about the amount of data sent to the Island.

For example, it's quite possible that the buy button doesn't need to receive image data.

The ideal approach is to send only the necessary data.

❌ Wrong Approach:

```tsx
import BuyButton from "$store/components/ui"
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){

return(
<div>
<Image src={product.image} width="100" height="100"/>
<BuyButton product={product} />
</div>
)
}
```

✅ Correct Approach:

```tsx
import BuyButton from "$store/components/ui"
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){

return(
<div>
<Image src={product.image} width="100" height="100"/>
<BuyButton id={product.id} seller={product.seller}/>
</div>
)
}
```

The correct approach sends only the ID and Seller data, which in this example are the only ones needed in the Island.

Thus, during hydration, the JSON that the Island will load won't be as large.

## Example Code - 2

In this example, we will show how to avoid sending a very large piece of data to a section.

Let's say we have an inline loader to fetch product colors and return them in a section.

```tsx
export default function Colors({colors}){

return colors.map(color => <span>{color}</span>)
}

export const loader = async () => {
const colors = await fetch("/product/colors").then(r => r.json())

return colors;
}
```

This component seems correct, right?

However, after an investigation, we found that the returned data also included product images.

Example of the API response:

```tsx
colors = [
{
"color": "red"
"images": [...]
},
{
"color": "green"
"images": [...]
},
{
"color": "orange"
"images": [...]
},
}]
```

The image data in this response will not be used in the section, so we don't need to send it.

We can filter it like this:


```tsx
export default function Colors({colors}){

return colors.map(color => <span>{color}</span>)
}

export const loader = async () => {
const result = await fetch("/product/colors").then(r => r.json())
const colors = result.map(item => item.color)
return colors;
}
```

This way, only the data that is used will be sent, avoiding unnecessary overload.

## Benefits
- Significant reduction in the size of transmitted JSON.
- Noticeable improvement in page performance, especially in terms of loading.

By implementing this data preprocessing process, it is possible to optimize page performance, ensuring that only crucial information is sent and processed, providing a more streamlined performance for the user.
159 changes: 159 additions & 0 deletions docs/performance/sending-only-necessary-data-to-client/pt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
descrição: Enviando apenas dados necessários ao cliente
since: 1.0.0
---

Ao carregar dados de APIs externas usando [Loaders](/docs/pt/concepts/loader) e enviá-los para a [Section](/docs/pt/concepts/section), é possível que o tamanho do _payload_ impacte negativamente a performance do site. O impacto ocorre tanto no tempo inicial de carregamento como também na [hidratação](https://blog.saeloun.com/2021/12/16/hydration/), onde a página é "inicializada" no browser para que possa ser interativa (usar `useEffect`, `useSignal`, etc...). É possível visualizar no tamanho do JSON final através da aba **Performance** no CMS deco.

![288067513-db3a14e1-c0ac-47f8-83b9-afc8db60de71](https://github.com/deco-sites/starting/assets/76822093/ec005f5d-4169-4e89-acd0-8c06baf3c80d)

Quando o tamanho do JSON passa de ~500kb, é provável que a UI não precise do dado completo, mas sim alguma parte dele (ou então uma computação sobre outros valores). Para diminuir esse tamanho e melhorar a performance da página, é possível **filtrar os dados** ainda no Loader para que apenas o necessário seja passado para a UI.


## Código de exemplo - 1

Nesse primeiro exemplo, mostraremos como evitar enviar muitos dados para uma Island.

Digamos que existe um componente chamado ProductCard, que recebe todo o JSON de um produto.

```tsx
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){
return(
<div>
<Image src={product.image} width="100" height="100"/>
</div>
)
}
```

Nele, você deseja incluir uma [Island](https://fresh.deno.dev/docs/concepts/islands) para criar o botão de comprar.

```tsx
import BuyButton from "$store/components/ui"
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){

return(
<div>
<Image src={product.image} width="100" height="100"/>
<BuyButton />
</div>
)
}
```

É possível que esse BuyButton, precise de algumas informações do produto para poder adicionar ao carrinho.

Aqui que devemos tomar cuidado a quantidade de dados enviados para a Island.

Por exemplo, é bem possível que o botão de comprar não precise receber dados de imagem.

O ideal é enviar apenas os dados necessários

❌ Abordagem errada:

```tsx
import BuyButton from "$store/components/ui"
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){

return(
<div>
<Image src={product.image} width="100" height="100"/>
<BuyButton product={product} />
</div>
)
}
```

✅ Abordagem correta:

```tsx
import BuyButton from "$store/components/ui"
import Image from "apps/website/components/Image.tsx"

export default function ProductCard({product} : Props){

return(
<div>
<Image src={product.image} width="100" height="100"/>
<BuyButton id={product.id} seller={product.seller}/>
</div>
)
}
```

A abordagem correta envia apenas os dados de ID e Seller, que no exemplo, são os únicos necessários na Island.

Assim, no momento de hidratação, o JSON que a Island irá carregar não será tão grande.

## Código de exemplo - 2

Neste exemplo, vamos mostrar como evitar enviar um dado muito grande para uma section.

Digamos que temos um loader inline para buscar as cores do produto e retornar em uma section.

```tsx
export default function Colors({colors}){

return colors.map(color => <span>{color}</span>)
}

export const loader = async () => {
const colors = await fetch("/product/colors").then(r => r.json())

return colors;
}
```

Esse componente parece correto, certo?

Porém, após uma investigação, verificamos que o dado retornado trazia também as imagens do produto.

Exemplo do retorno da API:

```tsx
colors = [
{
"color": "red"
"images": [...]
},
{
"color": "green"
"images": [...]
},
{
"color": "orange"
"images": [...]
},
}]
```

Os dados de imagem nesse retorno, não serão utilizados na section, então não precisamos enviá-los.

Podemos filtrar dessa forma:

```tsx
export default function Colors({colors}){

return colors.map(color => <span>{color}</span>)
}

export const loader = async () => {
const result = await fetch("/product/colors").then(r => r.json())
const colors = result.map(item => item.color)
return colors;
}
```

Dessa forma, apenas os dados utilizados serão enviados, evitando uma sobrecarga desnecessária.

## Benefícios
- Redução significativa no tamanho do JSON transmitido.
- Melhoria perceptível no desempenho da página, especialmente em termos de carregamento.

Ao implementar esse processo de pré-processamento de dados, é possível otimizar a performance da página, garantindo que apenas as informações cruciais sejam enviadas e processadas, proporcionando um desempenho mais otimizado para o usuário.
19 changes: 13 additions & 6 deletions docs/toc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,24 @@ const tableOfContents: TableOfContents = [
},
{
title: {
pt: "Como usar a deco para atingir alta performance",
en: "How can you use deco for achieve high performance",
pt: "Dicas para atingir uma alta performance",
en: "Performance tips",
},
slug: "performance/how-deco-performance",
slug: "performance/tips",
},
{
title: {
pt: "Dicas para atingir uma alta performance",
en: "Performance tips",
pt: "Enviar apenas os dados necessários para o cliente",
en: "Sending only necessary data to client",
},
slug: "performance/tips",
slug: "performance/sending-only-necessary-data-to-client",
},
{
title: {
pt: "Como usar a deco para atingir alta performance",
en: "How can you use deco for achieve high performance",
},
slug: "performance/how-deco-performance",
},
],
},
Expand Down