ABP Blazor 的 Bundling 和 Minification

Bundling(打包)指将多个JavaScript文件、CSS文件合并成一个或几个文件的过程。

Bundling的主要目的:

  • 减少HTTP请求:通过合并文件,可以减少浏览器需要发起的HTTP请求数量,从而加快首页加载速度。
  • 依赖管理:自动处理模块之间的依赖关系,确保代码正确执行。
  • 代码分割:支持将代码分割成多个块(chunks),按需加载,进一步优化性能。

Minification(压缩)指在不更改功能的情况下从代码中删除不必要的字符,例如将变量名称缩短为一个字符、删除注释和不必要的空格。

Minification的主要目的:

  • 减少文件体积:通过去除不必要的字符,可以显著减少文件的大小,从而减少传输的时间和带宽消耗。
  • 提高加载速度:较小的文件可以更快的加载,加快页面的加载速度。

有许多方法可以打包(bundling)和压缩(minification)客户端资源(JavaScript和CSS文件)。最常见的方法是:

ABP是模块化的,各个模块使用的脚本和样式需要在index.html文件中引用。ABP提供了一种内置的方式,简单、动态、模块化地执行Bundling和Minification。

ABP的App.razor文件如下:

<!DOCTYPE html>
<html lang="@CultureInfo.CurrentCulture.Name" dir="@rtl">
<head>
	...
	<AbpStyles BundleName="@BlazorLeptonXLiteThemeBundles.Styles.Global" WebAssemblyStyleFiles="GlobalStyles" @rendermode="InteractiveAuto" />
	...
</head>
<body class="abp-application-layout @rtl">
	...
    <AbpScripts BundleName="@BlazorLeptonXLiteThemeBundles.Scripts.Global" WebAssemblyScriptFiles="GlobalScripts" @rendermode="InteractiveAuto" />

    <script src="_framework/blazor.web.js"></script>

</body>
</html>
@code{
    private List<string> GlobalStyles =>
    [
        "global.css",
    ];

    private List<string> GlobalScripts =>
    [
        "global.js"
    ];
}

<AbpStyles>​标签将输出为:

<link rel="stylesheet" href=“...” />

<AbpScripts>​标签将输出为:

<script src="..." />

对于Blazor Server和Blazor WebAssembly,执行Bundling和Minification的原理有所不同。

Blazor Server 中

Blazor Server中的 Bundling 和 Minification 是动态执行的,它与ABP MVC的Bundling过程很相似(使用的标签不同)。通过在模块类中ConfigureServices​方法中配置AbpBundlingOptions​选项,添加js和css bundle。

需要添加Volo.Abp.AspNetCore.Mvc.UI.Bundling​NuGet包,默认情况下,这个包已经安装在启动模板中。因此,大多数时候,您不需要手动安装它。如果没有使用启动模板,可以使用ABP CLI将其安装到项目中。在项目文件夹(包含.csproj​文件的文件夹)中执行以下命令:

abp add-package Volo.Abp.AspNetCore.Mvc.UI.Bundling

创建一个新的Bundle

Configure<AbpBundlingOptions>(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"
			);
		});        
});

配置现有的Bundle

Configure<AbpBundlingOptions>(options =>
{
    options.StyleBundles.Configure(
        BlazorLeptonXLiteThemeBundles.Styles.Global,
        bundle =>
        {
            bundle.AddFiles("/blazor-global-styles.css");
            //You can remove the following line if you don't use Blazor CSS isolation for components
            bundle.AddFiles(new BundleFile("/Avalon.Blazor.styles.css", true));
            bundle.AddFiles(new BundleFile("/Avalon.Blazor.Client.styles.css", true));
        }
    );
});

Bundle Contributor

可以创建一个BundleContributor​的派生类,更清晰的管理js和css文件。

public class BlazorLeptonXLiteThemeScriptContributor : BundleContributor
{
    public override void ConfigureBundle(BundleConfigurationContext context)
    {
        context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap/js/bootstrap.bundle.js");
        context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/jquery/jquery.min.js");
        context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap-datepicker/js/bootstrap-datepicker.min.js");
    }
}
public class BlazorLeptonXLiteThemeStyleContributor : BundleContributor
{
    public override void ConfigureBundle(BundleConfigurationContext context)
    {
       context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/chart.js/Chart.min.css");
       context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap-datepicker/css/bootstrap-datepicker.min.css");
       context.Files.AddIfNotContains("/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/libs/bootstrap-icons/font/bootstrap-icons.css");

       var rtlPostfix = CultureHelper.IsRtl ? ".rtl" : string.Empty;
     
       context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/abp-bundle{rtlPostfix}.css");
       context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/blazor-bundle{rtlPostfix}.css");
       context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/bootstrap-dim{rtlPostfix}.css");
       context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/layout-bundle{rtlPostfix}.css");
       context.Files.AddIfNotContains($"/_content/Avalon.Abp.AspNetCore.Components.Web.LeptonXLiteTheme/side-menu/css/font-bundle{rtlPostfix}.css");
    }
}

在模块类中ConfigureServices​方法中如下配置AbpBundlingOptions​选项:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    Configure<AbpBundlingOptions>(options =>
    {
        options.StyleBundles
            .Add(BlazorLeptonXLiteThemeBundles.Styles.Global, bundle =>
            {
                bundle.AddContributors(typeof(BlazorLeptonXLiteThemeStyleContributor));
            });

        options.ScriptBundles
            .Add(BlazorLeptonXLiteThemeBundles.Scripts.Global, bundle =>
            {
                bundle.AddContributors(typeof(BlazorLeptonXLiteThemeScriptContributor));
            });
    });
}

Contributor依赖

一个bundle contributor可以对其他contributor有一个或多个依赖关系

[DependsOn(typeof(MyDependedBundleContributor))] //Define the dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
    //...
}

当添加一个bundle contributor时,它的依赖项会自动地递归添加。依赖根据依赖顺序添加并去重。

标准Package的Contributors

所有标准NPM包都有内置的contributor。例如,如果您的contributor依赖于bootstrap,您可以直接声明它,而不是自己添加bootstrap.css。

[DependsOn(typeof(BootstrapStyleContributor))] //Define the bootstrap style dependency
public class MyExtensionStyleBundleContributor : BundleContributor
{
    //...
}

使用标准包的内置contributors的优点:

  • 防止您键入无效的资源路径。
  • 防止在资源路径更改时更改您的contributor(依赖的contributor将处理它)。
  • 防止多个模块添加重复的文件。
  • 递归地管理依赖项(必要时添加依赖项的依赖项)。

需要按照Volo.Abp.AspNetCore.Mvc.UI.Packages​NuGet包。默认情况下,这个包已经安装在启动模板中。因此,大多数时候,您不需要手动安装它。如果没有使用启动模板,可以使用ABP CLI将其安装到项目中。在项目文件夹(包含.csproj​文件的文件夹)中执行以下命令:

abp add-package Volo.Abp.AspNetCore.Mvc.UI.Packages

访问IServiceProvider

虽然很少需要,但BundleConfigurationContext​有一个ServiceProvider​属性,你可以在ConfigureBundle​方法中解析服务依赖。

Bundle继承

在某些特定情况下,可能需要创建从其他bundle继承的新bundle。从一个bundle继承(递归地)将继承该bundle的所有文件和contributors。然后,派生的bundle可以添加或修改文件和contributors,而无需修改原始bundle。例子:

services.Configure<AbpBundlingOptions>(options =>
{
    options
        .StyleBundles
        .Add("MyTheme.MyGlobalBundle", bundle => {
            bundle
                .AddBaseBundles("MyGlobalBundle") //Can add multiple
                .AddFiles(
                    "/styles/mytheme-global-styles.css"
                );
        });
});

Bundling模式

development​环境中,ABP将包文件分开添加到页面中。ABP会为其他环境(staging​、production​……)自动进行打包和最小化。大多数情况下,这是你想要的行为。但是,在某些情况下,您可能需要手动配置它。有四种模式:

  • Auto​: 根据环境自动确定模式。
  • None​: 没有捆绑或缩小。
  • Bundle​: 捆绑但不缩小。
  • BundleAndMinify​: 捆绑和缩小。

你可以在模块的ConfigureServices​中配置AbpBundlingOptions​。

示例:

Configure<AbpBundlingOptions>(options =>
{
    options.Mode = BundlingMode.Bundle;
});

Blazor WebAssembly中

Blazor WebAssembly中的 Bundling 和 Minification 是不是动态执行的,而是通过bundle命令执行。

创建Bundle Contributor

在Blazor WebAssembly项目中,创建一个实现IBundleContributor​接口的类。

IBundleContributor​接口包含两个方法,都以BundleContext​作为参数:

  • AddScripts(...)
  • AddStyles(...)
public class MyProjectBundleContributor : IBundleContributor
{
	public void AddScripts(BundleContext context)
	{
		context.Add("site.js");
	}

	public void AddStyles(BundleContext context)
	{
		context.Add("main.css");
		context.Add("custom-styles.css");
	}
}

默认情况下,启动模板中有一个实现IBundleContributor​接口的<ProjectName>BundleContributor​类。所以,大多数时候,您不需要手动添加它。

配置

在Blazor WebAssembly项目的wwwroot/appsettings.json​文件的AbpCli.Bundle​节点中包含了Bundling配置。

示例如下:

{
  "AbpCli": {
    "Bundle": {
      "Mode": "BundleAndMinify", /* Options: None, Bundle, BundleAndMinify */
      "Name": "global",
      "IsBlazorWebApp": true,
      "InteractiveAuto": true,
      "Parameters": {
      }
    }
  }
}
  • Mode​:捆绑和缩小模式。可用的值有:

    • BundleAndMinify​:将所有文件捆绑到一个文件中,并缩小内容。
    • Bundle​:将所有文件捆绑成一个文件,但不要缩小。
    • None​:单独添加文件,不要捆绑。
  • Name​:包文件名。默认值为global​。

  • Parameters​:您可以在此部分中定义其他键/值对参数。abp bundle​命令自动将这些参数发送给bundle贡献者,您可以在bundle贡献者中检查这些参数,并根据这些值采取一些操作。例如想要从bundle中排除某些资源:

    public class MyProjectNameBundleContributor : IBundleContributor
    {
        public void AddScripts(BundleContext context)
        {
        }
    
        public void AddStyles(BundleContext context)
        {
            var excludeThemeFromBundle = bool.Parse(context.Parameters.GetValueOrDefault("ExcludeThemeFromBundle"));
            context.Add("mytheme.css", excludeFromBundle: excludeThemeFromBundle);
            context.Add("main.css");
        }
    }
    

bundle命令

在Blazor WebAssembly项目中执行bundle命令:

abp bundle

bundle命令将检测IBundleContributor​接口的实现类中包含的js和css,并读取appsettings.json​文件中的配置,根据配置打包资源,并更新index.html​文件。

例如,根据以上配置,将在wwwroot​生成global.css​和global.js​ bundle包,在App.razor文件文件的<AbpStyles>​标签和<AbpScripts>​标签的WebAssemblyScriptFiles​属性中引用了这两个bundle包。

posted @   星墨  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示