generated from deco-sites/start
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add docs 'sending only necessary data to client' (#213)
- Loading branch information
Showing
3 changed files
with
332 additions
and
6 deletions.
There are no files selected for viewing
160 changes: 160 additions & 0 deletions
160
docs/performance/sending-only-necessary-data-to-client/en.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
159
docs/performance/sending-only-necessary-data-to-client/pt.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters