From 2fdfe45cfbac3d6de5478822179b987ae25c19d3 Mon Sep 17 00:00:00 2001 From: "Ana Carolina D. C" <76822093+devartes@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:29:11 -0300 Subject: [PATCH] feat: add docs 'sending only necessary data to client' (#213) --- .../en.md | 160 ++++++++++++++++++ .../pt.md | 159 +++++++++++++++++ docs/toc.ts | 19 ++- 3 files changed, 332 insertions(+), 6 deletions(-) create mode 100644 docs/performance/sending-only-necessary-data-to-client/en.md create mode 100644 docs/performance/sending-only-necessary-data-to-client/pt.md diff --git a/docs/performance/sending-only-necessary-data-to-client/en.md b/docs/performance/sending-only-necessary-data-to-client/en.md new file mode 100644 index 00000000..157dde2b --- /dev/null +++ b/docs/performance/sending-only-necessary-data-to-client/en.md @@ -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( +
+ +
+ ) + } +``` + +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( +
+ + +
+ ) + } +``` + +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( +
+ + +
+ ) + } +``` + +✅ Correct Approach: + +```tsx + import BuyButton from "$store/components/ui" + import Image from "apps/website/components/Image.tsx" + + export default function ProductCard({product} : Props){ + + return( +
+ + +
+ ) + } +``` + +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 => {color}) +} + +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 => {color}) +} + +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. diff --git a/docs/performance/sending-only-necessary-data-to-client/pt.md b/docs/performance/sending-only-necessary-data-to-client/pt.md new file mode 100644 index 00000000..761f5ffd --- /dev/null +++ b/docs/performance/sending-only-necessary-data-to-client/pt.md @@ -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( +
+ +
+ ) + } +``` + +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( +
+ + +
+ ) + } +``` + +É 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( +
+ + +
+ ) + } +``` + +✅ Abordagem correta: + +```tsx + import BuyButton from "$store/components/ui" + import Image from "apps/website/components/Image.tsx" + + export default function ProductCard({product} : Props){ + + return( +
+ + +
+ ) + } +``` + +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 => {color}) +} + +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 => {color}) +} + +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. diff --git a/docs/toc.ts b/docs/toc.ts index 2cb70820..16f65d48 100644 --- a/docs/toc.ts +++ b/docs/toc.ts @@ -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", }, ], },