Resumo
Uma ilha determina um componente interativo e que será hidratado no lado do cliente. O servidor manda todos os dados das
props
de ilhas para fazer a hidratação, bem como o browser precisa de tempo para processar e renderizar essas ilhas.Por isso, é importante tomar alguns cuidados no uso de ilhas:
- Reduza ao máximo a quantidade de props a ser enviada / utilizada para uma ilha
- Torne uma ilha apenas o que for necessário, lembrando de usar o
children
para elementos internos que não precisam de hidratação.
Reduzindo o tamanho do JSON de props enviado para as ilhas
Ao carregar dados de APIs externas usando Loaders e
enviá-los para a 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, 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 de uma das páginas do seu site no CMS deco.
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.
Reduzindo dados enviados às ilhas
Nesse primeiro exemplo, mostraremos como evitar enviar muitos dados para uma ilha. Digamos que existe um componente chamado ProductCard, que recebe todo o JSON de um produto.
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 para criar o botão de comprar.
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 inadequada
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
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.
Reduzindo o escopo de uma ilha
Uma ilha e seus componentes serão todos hidratados do lado do cliente para poderem operar. Isto significa que, para todos os elementos definidos da ilha, eles serão recursivamente hidratados.
É possível reduzir o escopo da ilha, fazendo com que, qualquer elemento interno,
seja passado como children
da ilha.
❌ Abordagem inadequada
No exemplo abaixo, criamos uma ilha que interage com o localStorage
para
definir um título para uma galeria de itens. No exemplo abaixo, tanto os props
de gallery serão inseridos para hidratar o TitleContainer
, como serão também
inseridos para poder hidratar o Gallery
.
import { computed } from "@preact/signals";
import { IS_BROWSER } from "$fresh/runtime.ts";
import type { GalleryProps } from "../components/Gallery.tsx";
import { Gallery } from "../components/Gallery.tsx";
export default function TitleContainer(
{ galleryProps }: { galleryProps: GalleryProps },
) {
const title = computed(() => {
IS_BROWSER ? localStorage.getItem("title") : "Loading...";
});
return (
<div>
<h1>{title}</h1>
<Gallery {...galleryProps} />
</div>
);
}
✅ Abordagem correta
Se, no entanto, o Gallery
for repassado como children para a ilha, ele será
renderizado, serializado e não será hidratado! Para o TitleContainer
, o
children
é um html pronto para ser exibido, e, portanto, não é uma ilha em si.
import { computed } from "@preact/signals";
import type { ComponentChildren } from "preact";
import { IS_BROWSER } from "$fresh/runtime.ts";
export default function TitleContainer(
{ children }: { children: ComponentChildren },
) {
const title = computed(() => {
IS_BROWSER ? localStorage.getItem("title") : "Loading...";
});
return (
<div>
<h1>{title}</h1>
{children}
</div>
);
}
Uso do title container (em uma section, por exemplo):
//...
<TitleContainer>
<Gallery {...galleryProps}>
</TitleContainer>
//...