# ASP.NET Core MVC Bundling & Minification Existem várias maneiras de agrupar e minificar recursos do lado do cliente (arquivos JavaScript e CSS). As formas mais comuns são: * Usando a extensão do Visual Studio [Bundler & Minifier](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.BundlerMinifier) ou o [pacote NuGet](https://www.nuget.org/packages/BuildBundlerMinifier/). * Usando os gerenciadores de tarefas [Gulp](https://gulpjs.com/)/[Grunt](https://gruntjs.com/) e seus plugins. O ABP oferece uma maneira simples, dinâmica, poderosa, modular e integrada. ## Pacote Volo.Abp.AspNetCore.Mvc.UI.Bundling > Este pacote já está instalado por padrão nos modelos de inicialização. Portanto, na maioria das vezes, você não precisa instalá-lo manualmente. Se você não estiver usando um modelo de inicialização, pode usar o [ABP CLI](../../CLI.md) para instalá-lo em seu projeto. Execute o seguinte comando na pasta que contém o arquivo .csproj do seu projeto: ```` abp add-package Volo.Abp.AspNetCore.Mvc.UI.Bundling ```` > Se você ainda não o fez, primeiro precisa instalar o [ABP CLI](../../CLI.md). Para outras opções de instalação, consulte [a página de descrição do pacote](https://abp.io/package-detail/Volo.Abp.AspNetCore.Mvc.UI.Bundling). ## Tag Helpers de Agrupamento Razor A maneira mais simples de criar um pacote é usar os tag helpers `abp-script-bundle` ou `abp-style-bundle`. Exemplo: ````html ```` Este pacote define um pacote de estilo com um **nome único**: `MyGlobalBundle`. É muito fácil entender como usá-lo. Vamos ver como ele *funciona*: * O ABP cria o pacote como **lazy** a partir dos arquivos fornecidos quando é **solicitado pela primeira vez**. Para as chamadas subsequentes, ele é retornado do **cache**. Isso significa que, se você adicionar condicionalmente os arquivos ao pacote, ele será executado apenas uma vez e quaisquer alterações na condição não afetarão o pacote para as próximas solicitações. * O ABP adiciona os arquivos do pacote **individualmente** à página para o ambiente `development`. Ele agrupa e minifica automaticamente para outros ambientes (`staging`, `production`...). Consulte a seção *Modo de Agrupamento* para alterar esse comportamento. * Os arquivos do pacote podem ser arquivos **físicos** ou [**virtuais/embutidos**](../../Virtual-File-System.md). * O ABP adiciona automaticamente uma **string de consulta de versão** ao URL do arquivo do pacote para evitar que os navegadores armazenem em cache quando o pacote está sendo atualizado. (como ?_v=67872834243042 - gerado a partir da última data de alteração dos arquivos relacionados). A versão funciona mesmo se os arquivos do pacote forem adicionados individualmente à página (no ambiente de desenvolvimento). ### Importando os Tag Helpers de Agrupamento > Isso já é importado por padrão nos modelos de inicialização. Portanto, na maioria das vezes, você não precisa adicioná-lo manualmente. Para usar os tag helpers de pacote, você precisa adicioná-los ao seu arquivo `_ViewImports.cshtml` ou à sua página: ```` @addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling ```` ### Pacotes sem nome O nome é **opcional** para os tag helpers de pacote razor. Se você não definir um nome, ele será **calculado** automaticamente com base nos nomes dos arquivos do pacote usados (eles são **concatenados** e **hashados**). Exemplo: ````html @if (ViewBag.IncludeCustomStyles != false) { } ```` Isso pode criar **dois pacotes diferentes** (um inclui o `my-global-style.css` e o outro não). Vantagens de pacotes **sem nome**: * É possível **adicionar itens condicionalmente** ao pacote. Mas isso pode levar a várias variações do pacote com base nas condições. Vantagens de pacotes **com nome**: * Outros **módulos podem contribuir** para o pacote pelo nome (consulte as seções abaixo). ### Arquivo Único Se você precisa apenas adicionar um único arquivo à página, pode usar a tag `abp-script` ou `abp-style` sem envolvê-la na tag `abp-script-bundle` ou `abp-style-bundle`. Exemplo: ````xml ```` O nome do pacote será *scripts.my-scripts* para o exemplo acima ("/" é substituído por "."). Todos os recursos de agrupamento funcionam como esperado para pacotes de arquivo único também. ## Opções de Agrupamento Se você precisa usar o mesmo pacote em **múltiplas páginas** ou deseja usar alguns recursos mais **poderosos**, você pode configurar pacotes **por código** em sua classe de [módulo](../../Module-Development-Basics.md). ### Criando um Novo Pacote Exemplo de uso: ````C# [DependsOn(typeof(AbpAspNetCoreMvcUiBundlingModule))] public class MyWebModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => { options .ScriptBundles .Add("MyGlobalBundle", bundle => { bundle.AddFiles( "/libs/jquery/jquery.js", "/libs/bootstrap/js/bootstrap.js", "/libs/toastr/toastr.min.js", "/scripts/my-global-scripts.js" ); }); }); } } ```` > Você pode usar o mesmo nome (*MyGlobalBundle* aqui) para um pacote de script e estilo, pois eles são adicionados a coleções diferentes (`ScriptBundles` e `StyleBundles`). Após definir um pacote desse tipo, ele pode ser incluído em uma página usando os mesmos tag helpers definidos acima. Exemplo: ````html ```` Desta vez, nenhum arquivo é definido na definição do tag helper porque os arquivos do pacote são definidos pelo código. ### Configurando um Pacote Existente O ABP também oferece suporte à [modularidade](../../Module-Development-Basics.md) para agrupamento. Um módulo pode modificar um pacote existente criado por um módulo dependente. Exemplo: ````C# [DependsOn(typeof(MyWebModule))] public class MyWebExtensionModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => { options .ScriptBundles .Configure("MyGlobalBundle", bundle => { bundle.AddFiles( "/scripts/my-extension-script.js" ); }); }); } } ```` Você também pode usar o método `ConfigureAll` para configurar todos os pacotes existentes: ````C# [DependsOn(typeof(MyWebModule))] public class MyWebExtensionModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => { options .ScriptBundles .ConfigureAll(bundle => { bundle.AddFiles( "/scripts/my-extension-script.js" ); }); }); } } ```` ## Contribuidores de Pacotes Adicionar arquivos a um pacote existente é útil. E se você precisar **substituir** um arquivo no pacote ou quiser adicionar arquivos **condicionalmente**? Definir um contribuidor de pacote fornece poder extra para esses casos. Um exemplo de contribuidor de pacote que substitui o bootstrap.css por uma versão personalizada: ````C# public class MyExtensionGlobalStyleContributor : BundleContributor { public override void ConfigureBundle(BundleConfigurationContext context) { context.Files.ReplaceOne( "/libs/bootstrap/css/bootstrap.css", "/styles/extensions/bootstrap-customized.css" ); } } ```` Em seguida, você pode usar esse contribuidor da seguinte maneira: ````C# services.Configure(options => { options .ScriptBundles .Configure("MyGlobalBundle", bundle => { bundle.AddContributors(typeof(MyExtensionGlobalStyleContributor)); }); }); ```` > Você também pode adicionar contribuidores ao criar um novo pacote. Os contribuidores também podem ser usados nos tag helpers de pacote. Exemplo: ````xml ```` As tags `abp-style` e `abp-script` podem receber atributos `type` (em vez de atributos `src`) como mostrado neste exemplo. Quando você adiciona um contribuidor de pacote, suas dependências também são adicionadas automaticamente ao pacote. ### Dependências de Contribuidores Um contribuidor de pacote pode ter uma ou mais dependências de outros contribuidores. Exemplo: ````C# [DependsOn(typeof(MyDependedBundleContributor))] //Define a dependência public class MyExtensionStyleBundleContributor : BundleContributor { //... } ```` Quando um contribuidor de pacote é adicionado, suas dependências são adicionadas **automaticamente e recursivamente**. As dependências são adicionadas pela **ordem de dependência** evitando **duplicatas**. As duplicatas são evitadas mesmo que estejam em pacotes separados. O ABP organiza todos os pacotes em uma página e elimina duplicações. Criar contribuidores e definir dependências é uma maneira de organizar a criação de pacotes em diferentes módulos. ### Extensões de Contribuidores Em alguns cenários avançados, você pode querer fazer alguma configuração adicional sempre que um contribuidor de pacote for usado. As extensões de contribuidor funcionam perfeitamente quando o contribuidor estendido é usado. O exemplo abaixo adiciona alguns estilos para a biblioteca prism.js: ````csharp public class MyPrismjsStyleExtension : BundleContributor { public override void ConfigureBundle(BundleConfigurationContext context) { context.Files.AddIfNotContains("/libs/prismjs/plugins/toolbar/prism-toolbar.css"); } } ```` Em seguida, você pode configurar `AbpBundleContributorOptions` para estender o `PrismjsStyleBundleContributor` existente. ````csharp Configure(options => { options .Extensions() .Add(); }); ```` Sempre que `PrismjsStyleBundleContributor` for adicionado a um pacote, `MyPrismjsStyleExtension` também será adicionado automaticamente. ### Acessando o IServiceProvider Embora raramente seja necessário, `BundleConfigurationContext` possui uma propriedade `ServiceProvider` que permite resolver dependências de serviço dentro do método `ConfigureBundle`. ### Contribuidores de Pacotes Padrão Adicionar um recurso específico do pacote NPM (arquivos js, css) a um pacote é bastante simples para esse pacote. Por exemplo, você sempre adiciona o arquivo `bootstrap.css` para o pacote NPM do bootstrap. Existem contribuidores embutidos para todos os [pacotes NPM padrão](Client-Side-Package-Management.md). Por exemplo, se seu contribuidor depende do bootstrap, você pode apenas declará-lo, em vez de adicionar o bootstrap.css você mesmo. ````C# [DependsOn(typeof(BootstrapStyleContributor))] //Define a dependência de estilo do bootstrap public class MyExtensionStyleBundleContributor : BundleContributor { //... } ```` Usando os contribuidores embutidos para pacotes padrão; * Impede que você digite **os caminhos de recursos inválidos**. * Impede a alteração do seu contribuidor se o **caminho do recurso mudar** (o contribuidor dependente lidará com isso). * Impede que vários módulos adicionem **arquivos duplicados**. * Gerencia **dependências recursivamente** (adiciona dependências de dependências, se necessário). #### Pacote Volo.Abp.AspNetCore.Mvc.UI.Packages > Este pacote já está instalado por padrão nos modelos de inicialização. Portanto, na maioria das vezes, você não precisa instalá-lo manualmente. Se você não estiver usando um modelo de inicialização, pode usar o [ABP CLI](../../CLI.md) para instalá-lo em seu projeto. Execute o seguinte comando na pasta que contém o arquivo .csproj do seu projeto: ```` abp add-package Volo.Abp.AspNetCore.Mvc.UI.Packages ```` > Se você ainda não o fez, primeiro precisa instalar o [ABP CLI](../../CLI.md). Para outras opções de instalação, consulte [a página de descrição do pacote](https://abp.io/package-detail/Volo.Abp.AspNetCore.Mvc.UI.Packages). ### Herança de Pacotes Em alguns casos específicos, pode ser necessário criar um **novo** pacote **herdado** de outro(s) pacote(s). Ao herdar de um pacote (recursivamente), todos os arquivos/contribuidores desse pacote são herdados. Em seguida, o pacote derivado pode adicionar ou modificar arquivos/contribuidores **sem modificar** o pacote original. Exemplo: ````c# services.Configure(options => { options .StyleBundles .Add("MyTheme.MyGlobalBundle", bundle => { bundle .AddBaseBundles("MyGlobalBundle") //Pode adicionar vários .AddFiles( "/styles/mytheme-global-styles.css" ); }); }); ```` ## Opções Adicionais Esta seção mostra outras opções úteis para o sistema de agrupamento e minificação. ### Modo de Agrupamento O ABP adiciona arquivos de pacote individualmente à página para o ambiente `development`. Ele agrupa e minifica automaticamente para outros ambientes (`staging`, `production`...). Na maioria das vezes, esse é o comportamento desejado. No entanto, você pode configurá-lo manualmente em alguns casos. Existem quatro modos; * `Auto`: Determina automaticamente o modo com base no ambiente. * `None`: Sem agrupamento ou minificação. * `Bundle`: Agrupado, mas não minificado. * `BundleAndMinify`: Agrupado e minificado. Você pode configurar `AbpBundlingOptions` no `ConfigureServices` do seu [módulo](../../Module-Development-Basics.md). **Exemplo:** ````csharp Configure(options => { options.Mode = BundlingMode.Bundle; }); ```` ### Ignorar para Minificação É possível ignorar um arquivo específico para a minificação. **Exemplo:** ````csharp Configure(options => { options.MinificationIgnoredFiles.Add("/scripts/myscript.js"); }); ```` O arquivo fornecido ainda é adicionado ao pacote, mas não é minificado neste caso. ### Carregar JavaScript e CSS de forma assíncrona Você pode configurar `AbpBundlingOptions` para carregar todos ou um único arquivo js/css de forma assíncrona. **Exemplo:** ````csharp Configure(options => { options.PreloadStyles.Add("/__bundles/Basic.Global"); options.DeferScriptsByDefault = true; }); ```` **HTML de saída:** ````html ```` ### Suporte a Arquivos Externos/CDN O sistema de agrupamento reconhece automaticamente os arquivos externos/CDN e os adiciona à página sem nenhuma alteração. #### Usando Arquivos Externos/CDN em `AbpBundlingOptions` ````csharp Configure(options => { options.StyleBundles .Add("MyStyleBundle", configuration => { configuration .AddFiles("/styles/my-style1.css") .AddFiles("/styles/my-style2.css") .AddFiles("https://cdn.abp.io/bootstrap.css") .AddFiles("/styles/my-style3.css") .AddFiles("/styles/my-style4.css"); }); options.ScriptBundles .Add("MyScriptBundle", configuration => { configuration .AddFiles("/scripts/my-script1.js") .AddFiles("/scripts/my-script2.js") .AddFiles("https://cdn.abp.io/bootstrap.js") .AddFiles("/scripts/my-script3.js") .AddFiles("/scripts/my-script4.js"); }); }); ```` **HTML de saída:** ````html ```` #### Usando Arquivos Externos/CDN em Tag Helpers. ````html ```` **HTML de saída:** ````html ```` ## Temas Os temas usam os contribuidores de pacotes padrão para adicionar recursos de biblioteca aos layouts de página. Os temas também podem definir alguns pacotes padrão/globais, para que qualquer módulo possa contribuir para esses pacotes padrão/globais. Consulte a documentação de [temas](Theming.md) para mais informações. ## Melhores Práticas e Sugestões É sugerido definir vários pacotes para uma aplicação, cada um usado para diferentes propósitos. * **Pacote global**: Pacotes de estilo/script globais são incluídos em todas as páginas da aplicação. Os temas já definem pacotes de estilo e script globais. Seu módulo pode contribuir para eles. * **Pacotes de layout**: Este é um pacote específico para um layout individual. Contém apenas recursos compartilhados entre todas as páginas que usam o layout. Use os tag helpers de agrupamento para criar o pacote como uma boa prática. * **Pacotes de módulo**: Para recursos compartilhados entre as páginas de um módulo individual. * **Pacotes de página**: Pacotes específicos criados para cada página. Use os tag helpers de agrupamento para criar o pacote como uma melhor prática. Estabeleça um equilíbrio entre desempenho, uso de largura de banda da rede e quantidade de pacotes. ## Veja também * [Gerenciamento de Pacotes do Lado do Cliente](Client-Side-Package-Management.md) * [Temas](Theming.md)