-
Notifications
You must be signed in to change notification settings - Fork 1
Assets pt BR
🌐 Esta página em: English · Português
O eQuantic.UI oferece um sistema declarativo de dependências de assets que permite aos componentes declararem os scripts e folhas de estilo de que precisam. Os assets são coletados automaticamente durante o SSR, deduplicados e injetados no <head> da página.
O sistema resolve um problema comum: componentes que dependem de bibliotecas externas (Prism.js, Chart.js, etc.) precisam que os scripts/CSS deles sejam carregados, mas não deveriam embutir tags <script> inline no HTML renderizado. Em vez disso, eles declaram dependências, e o framework cuida da injeção.
Percurso da árvore de componentes → Coleta IRequireAssets → Coleta IComponentAssetProvider<T> → Deduplica → Injeta no <head>
Todos os tipos estão em eQuantic.UI.Core.Assets.
Interface base para todos os tipos de asset.
public interface IAsset
{
string Key { get; } // Chave única para deduplicação
string? Id { get; } // Id HTML opcional para manipulação no cliente
string Render(); // Renderiza como tag HTML
}| Tipo | Saída | Formato da chave |
|---|---|---|
ScriptAsset |
<script src="..." defer></script> |
script:{Src} |
InlineScriptAsset |
<script>...</script> |
inline-script:{hash} |
StylesheetAsset |
<link rel="stylesheet" href="..."> |
stylesheet:{Href} |
InlineStyleAsset |
<style>...</style> |
inline-style:{hash} |
Todos os tipos aceitam um parâmetro Id opcional para manipulação do DOM no cliente:
new StylesheetAsset("https://cdn.example.com/theme.css", Id: "theme-css")
// Renderiza: <link rel="stylesheet" href="https://cdn.example.com/theme.css" id="theme-css">Builder fluente passado aos componentes para declarar dependências:
public class AssetBuilder
{
AssetBuilder AddScript(string src, bool defer = true, string? id = null);
AssetBuilder AddInlineScript(string content, string? id = null);
AssetBuilder AddStylesheet(string href, string? id = null);
AssetBuilder AddInlineStyle(string content, string? id = null);
}Coleta e deduplica os assets. A ordem de renderização é otimizada: CSS primeiro, depois JS (a ordem correta de bloqueio de render).
StylesheetAsset → InlineStyleAsset → ScriptAsset → InlineScriptAsset
A deduplicação usa TryAdd com a Key do asset, então o primeiro registro vence.
Para componentes que são donos das próprias dependências. O componente declara ele mesmo o que precisa.
public class CodeBlock : StatelessComponent, IRequireAssets
{
public void ConfigureAssets(AssetBuilder assets)
{
assets.AddStylesheet(
"https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css",
id: "prism-theme");
assets.AddScript("https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js");
}
}Quando usar:
- Componentes do framework (CodeBlock, gráficos, etc.)
- Componentes cujas dependências são intrínsecas
- O desenvolvedor que usa o componente não precisa saber das bibliotecas por baixo
Para associar assets a componentes externamente, tipicamente componentes de terceiros que não implementam IRequireAssets.
public class ChartJsAssetProvider : IComponentAssetProvider<ChartCanvas>
{
public void ConfigureAssets(AssetBuilder assets)
{
assets.AddScript("https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js");
}
}Registro (escolha um):
// Opção 1: varredura automática (descoberto sozinho nos assemblies varridos)
options.ScanAssembly(typeof(Program).Assembly);
// Opção 2: registro explícito via UIOptions
options.WithAssetProvider<ChartJsAssetProvider>();
// Opção 3: registro manual na DI
services.AddSingleton<IComponentAssetProvider<ChartCanvas>, ChartJsAssetProvider>();Quando usar:
- Componentes de terceiros que você não pode modificar
- Sobreposições no nível do app para assets de componentes do framework
- Componentes vindos de pacotes externos
Quando os dois padrões existem para o mesmo tipo de componente:
- O
IRequireAssetsexecuta primeiro (padrão do componente) - O
IComponentAssetProvider<T>executa depois (provedor externo)
A deduplicação é por Key, com semântica de o primeiro vence. Se você precisa que o provedor sobreponha o asset padrão de um componente, use uma Key diferente (por exemplo, outra URL).
Durante a renderização no servidor, o ServerRenderingService percorre a árvore de componentes:
RenderPageAsync()
├── Cria a AssetCollection
├── CollectAssets(rootComponent, assets, services, visited)
│ ├── Verifica IRequireAssets → ConfigureAssets()
│ ├── Verifica a DI por IComponentAssetProvider<T> → ConfigureAssets()
│ ├── Recursa em component.Children
│ └── Para StatelessComponent → Build() → recursa no resultado
├── Renderiza o HTML
└── Devolve o ServerRenderResult com os Assets
Os assets são então mesclados em HtmlShellOptions.HeadTags antes de servir a página.
Comportamentos-chave:
-
Deduplicação por tipo: cada tipo de componente é processado uma vez (via
HashSet<Type>) -
Deduplicação por asset: cada chave de asset é registrada uma vez (via
Dictionary.TryAdd) - Custo zero: páginas sem componentes que exigem assets não ganham tag extra nenhuma
-
Degradação graciosa: se o
Build()falhar (por exemplo, DI faltando), o componente é pulado
As implementações de IComponentAssetProvider<T> são descobertas automaticamente durante o AddUI():
builder.Services.AddUI(options =>
{
options.ScanAssembly(typeof(Program).Assembly); // Acha os provedores aqui sozinho
});A varredura procura todas as classes não abstratas que implementam IComponentAssetProvider<T> nos assemblies varridos e as registra como singletons via TryAddSingleton.
Registros explícitos via WithAssetProvider<T>() têm prioridade sobre os varridos automaticamente (são registrados primeiro).
O componente CodeBlock demonstra o padrão completo:
public class CodeBlock : StatelessComponent, IRequireAssets
{
public void ConfigureAssets(AssetBuilder assets)
{
// Folha de estilo com id para troca dinâmica de tema
assets.AddStylesheet(
"https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css",
id: "prism-theme");
// Scripts principais do Prism.js
assets.AddScript("https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.min.js");
assets.AddScript("https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js");
// Funções utilitárias + troca automática de tema claro/escuro
assets.AddInlineScript(
"function copyToClipboard(id){...}" +
"function toggleCodeBlock(id){...}" +
"(function(){" +
"function updateTheme(){...}" + // Alterna entre prism.css e prism-tomorrow.css
"updateTheme();" +
"new MutationObserver(function(){updateTheme()})" +
".observe(document.documentElement,{attributes:true,attributeFilter:['class']});" +
"})();"
);
}
}O desenvolvedor só usa new CodeBlock(code, "csharp"), e os scripts do Prism.js, o CSS, a troca de tema e as funções utilitárias são todos tratados automaticamente.
Desde 0.2.0-preview.1
O ícone do lançador é um componente como qualquer outro, no mesmo vocabulário, então ele não consegue divergir da marca com que o app já desenha:
public sealed class AppIcon : IAppIcon
{
public VisualNode Build(ComponentContext context) =>
Box(new BoxStyle { Background = Brand, CornerRadius = new CornerRadii(0) },
Text("eQ", TypeRole.Display, Ink).Centered());
}Duas cercas, ambas vindas do meio e não do framework:
- Preencha o quadrado inteiro, opaco. Um ícone transparente é composto contra o que quer que o lançador esteja mostrando, que nunca é aquilo contra o que você desenhou.
- Sem estado, sem interação. Ele é construído uma vez; um handler de toque num ícone é um handler que ninguém alcança. Fique em formas, gradientes e no máximo uma letra ou duas, porque no tamanho em que uma tela inicial de fato mostra, qualquer coisa mais fina vira borrão.
O build o rasteriza em cada tamanho que cada plataforma quer, e liga os do web no head sem o app precisar dizer nada.
🌐 English · Português
🏁 Comece aqui
📱 Write-once
- Componentes write-once
- Superfície declarativa
- Motor Photon
- Design System
- Capacidades
- Armazenamento
- Formulários
- Editor de código
- Markdown
- Mermaid
- Renderização de Email
🏗️ Arquitetura
⚙️ Compilação
- Compilador
- Avaliação em tempo de compilação
- Recursos C# suportados
- Resolução de tipos externos
- Fluxo de build
- Diagnósticos
⚡ Runtime
🔌 Servidor
🎨 Ecossistema
🚀 Desenvolvimento