Blazor WebAssembly加載優化方案
對于Blazor WebAssembly加載方案的優化是針對于WebAssembly首次加載,由于BlazorWebAssembly是在首次加載的時候會将.NET Core的所有程式集都會加載到浏覽器中,并且在使用的時候可能引用了很多第三方的dll,導緻加載緩慢;
優化方案 :
1. 壓縮
釋出 Blazor WebAssembly 應用時,将在釋出過程中對輸出内容進行靜态壓縮,進而減小應用的大小,并免去運作時壓縮的開銷。 使用以下壓縮算法:
- Brotli(級别最高)
- Gzip
從 google/brotli GitHub repository 中擷取 JavaScript Brotli 解碼器。 縮小的解碼器檔案被命名為
decode.min.js
,并且位于存儲庫的
js
檔案夾中。
修改
wwwroot/index.html
檔案代碼 ,添加autostart=“false” ,阻住預設啟動加載程式集
在 Blazor 的
<script>
标記之後和結束
</body>
标記之前,添加以下 JavaScript 代碼
<script>
塊:
<script type="module">
import { BrotliDecode } from './decode.min.js';
Blazor.start({
loadBootResource: function (type, name, defaultUri, integrity) {
// 注意:這裡使用localhost的時候不會啟動壓縮
if (type !== 'dotnetjs' && location.hostname !== 'localhost') {
return (async function () {
const response = await fetch(defaultUri + '.br', { cache: 'no-cache' });
if (!response.ok) {
throw new Error(response.statusText);
}
const originalResponseBuffer = await response.arrayBuffer();
const originalResponseArray = new Int8Array(originalResponseBuffer);
const decompressedResponseArray = BrotliDecode(originalResponseArray);
const contentType = type ===
'dotnetwasm' ? 'application/wasm' : 'application/octet-stream';
return new Response(decompressedResponseArray,
{ headers: { 'content-type': contentType } });
})();
}
}
});
</script>
壓縮方案将減少加載時間,大概是壓縮dll的三分之一大小,效果如圖
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-gemfSgVA-1676015504451)(https://foruda.gitee.com/images/1675065716253529521/2e337fd6_8229157.png “螢幕截圖”)]
在使用
autostart="false"
标記以後不會啟動就加載,加載程式集将在上面的代碼塊中執行,預設是加載br;
2. 延遲加載程式集
通過等待應用程式集直到需要時才加載,提高 Blazor WebAssembly 應用啟動性能,這種方式稱為“延遲加載”。
将BlazorWebAssembly項目拆分細緻,通過延遲加載程式集提升BlazorWebAssembly首次加載時間,我們将通過一個案例來講解延遲加載程式集
建立一個空的Blazor WebAssembly項目: 項目名稱
Demand
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-oeVLPhzW-1676015504452)(https://foruda.gitee.com/images/1675065709167081163/a9e98e61_8229157.png “螢幕截圖”)]
取消
HTTPS
使用漸進式Web應用程式
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-hiSh83QB-1676015504452)(https://foruda.gitee.com/images/1675065704598198762/56cdbf29_8229157.png “螢幕截圖”)]
在建立Razor類庫,項目名稱:
Demand.Components
,然後預設選項建立項目
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-uZbSYYRb-1676015504453)(https://foruda.gitee.com/images/1675065696314750567/ae8155ac_8229157.png “螢幕截圖”)]
建立
Components.razor
檔案,并且删除多餘檔案,效果如圖:
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-bvxcR04i-1676015504453)(https://foruda.gitee.com/images/1675065691848164281/cafa162d_8229157.png “螢幕截圖”)]
在
Components.razor
添加以下代碼:
@inject NavigationManager NavigationManager
@page "/components"
<div>
<h1>
Components
</h1>
</div>
<button @onclick="Goto">跳轉到首頁</button>
@code
{
private void Goto()
{
NavigationManager.NavigateTo("/");
}
}
在
Demand
項目中引用
Demand.Components
項目
修改
App.razor
檔案 ,代碼如下:
@using System.Reflection
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@*
這裡需要注意,WebAssembly是預設注入的但是Server并沒有注入
在Server中手動注入
builder.Services.AddScoped<LazyAssemblyLoader>();
*@
@inject LazyAssemblyLoader AssemblyLoader
<Router AppAssembly="@typeof(App).Assembly"
AdditionalAssemblies="@lazyLoadedAssemblies"
OnNavigateAsync="@OnNavigateAsync">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
@code {
private List<Assembly> lazyLoadedAssemblies = new();
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "components")
{
// 這裡自定義Demand.Components依賴的程式集,
var assemblies = await AssemblyLoader.LoadAssembliesAsync(new[] { "Demand.Components.dll" });
// 添加到路由程式集掃描中
lazyLoadedAssemblies.AddRange(assemblies);
}
}
catch (Exception ex)
{
}
}
}
處理指定路由元件需要加載的程式集
打開
Demand
項目檔案
如果在Debug模式下可以使用添加以下忽略清單:
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="System.Xml.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XmlSerializer.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XmlDocument.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XPath.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XPath.XDocument.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.XDocument.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.Serialization.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.ReaderWriter.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Xml.Linq.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Windows.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Quic.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.ZipFile.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Runtime.Numerics.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Collections.Immutable.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Win32.Registry.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Web.HttpUtility.dll" />
<BlazorWebAssemblyLazyLoad Include="System.ValueTuple.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.AccessControl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Mail.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.NameResolution.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.UnmanagedMemoryStream.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Pipes.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Pipes.AccessControl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Pipelines.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.Watcher.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.DriveInfo.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.FileSystem.AccessControl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Data.Common.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.CSharp.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Console.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Core.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Data.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Data.DataSetExtensions.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Drawing.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Drawing.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.TraceSource.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Tools.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.TextWriterTraceListener.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.StackTrace.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Process.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.FileVersionInfo.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.DiagnosticSource.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Debug.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Diagnostics.Contracts.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.AspNetCore.Authorization.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.AspNetCore.Components.Forms.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.AspNetCore.Metadata.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.Configuration.Binder.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.FileProviders.Abstractions.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.FileProviders.Physical.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.Configuration.FileExtensions.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Extensions.FileSystemGlobbing.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.MemoryMappedFiles.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.IsolatedStorage.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.FileSystem.dll" />
<BlazorWebAssemblyLazyLoad Include="System.IO.Compression.Brotli.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Formats.Tar.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Formats.Asn1.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebSockets.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Private.DataContractSerialization.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Private.Xml.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.VisualBasic.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.VisualBasic.Core.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Threading.Tasks.Dataflow.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Text.Encoding.CodePages.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebSockets.Client.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Private.Xml.Linq.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Text.RegularExpressions.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Sockets.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebClient.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebProxy.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Ping.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.X509Certificates.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.WebHeaderCollection.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.OpenSsl.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Encoding.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Csp.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Cng.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Claims.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Security.Cryptography.Algorithms.dll" />
<BlazorWebAssemblyLazyLoad Include="Microsoft.Win32.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.HttpListener.dll" />
<BlazorWebAssemblyLazyLoad Include="System.AppContext.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.NetworkInformation.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Requests.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Primitives.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Security.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.ServicePoint.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Http.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Globalization.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Globalization.Calendars.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Globalization.Extensions.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Net.Http.Json.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Web.dll" />
<BlazorWebAssemblyLazyLoad Include="WindowsBase.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Resources.Writer.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Resources.ResourceManager.dll" />
<BlazorWebAssemblyLazyLoad Include="System.Resources.Reader.dll" />
</ItemGroup>
這些是不常用的一些程式集,如果出現以下錯誤,請将找不到的程式集删除按需加載配置,
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-Ao9q8dAE-1676015504453)(https://foruda.gitee.com/images/1675065662767429495/62e04099_8229157.png “螢幕截圖”)]
但是如果使用了上面的按需加載配置,在釋出的時候會出現異常比如下面這個圖這樣;錯誤原因是Blazor WebAssembly在釋出的時候預設使用裁剪,由于以下程式集剛剛好是沒有使用的,在裁剪以後會配置按需加載,但是它已經被裁剪了,是以導緻無法找到按需加載的程式集;隻要删除報錯的程式集即可;這個隻有在釋出的時候才會出現,DeBug還是可以繼續使用上面的按需加載的配置,可以在調試的時候響應更快
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-JySGml6H-1676015504454)(https://foruda.gitee.com/images/1675065669456522621/e7e0195e_8229157.png “螢幕截圖”)]
然後下一步
添加指定項目的按需加載配置,我們将
Demand.Components
項目配置上去,
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="Demand.Components.dll" />
</ItemGroup>
修改
Pages/Index.razor
檔案代碼
@page "/"
@inject NavigationManager NavigationManager
<h1>Hello, world!</h1>
<button @onclick="Goto">跳轉到components</button>
@code
{
private void Goto()
{
NavigationManager.NavigateTo("/components");
}
}
然後啟動項目,打開F12開發者調試工具,點選
應用程式
,找到存儲,點選清除網站資料(第一次加載以後程式集會緩存起來):
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-juz04kbZ-1676015504454)(https://foruda.gitee.com/images/1675065647531740783/161df063_8229157.png “螢幕截圖”)]
點選網絡,然後重新整理界面,我們看到這裡并不會加載
Demand.Components.dll
,但是這裡的程式集:
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-EcTMxaur-1676015504454)(https://foruda.gitee.com/images/1675065640377404774/06b34075_8229157.png “螢幕截圖”)]
然後點選界面的按鈕:
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-eeMMpnf0-1676015504454)(https://foruda.gitee.com/images/1675065628605783263/65556ef2_8229157.png “螢幕截圖”)]
這個時候在來到
調試工具的網絡
,我們看到
Demand.Components.dll
已經被加載了,當我們使用的時候這個程式集才會加載,并且第二次加入界面的時候不會重複加載程式集
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-c0d6Uht1-1676015504455)(https://foruda.gitee.com/images/1675065615077323202/f4068427_8229157.png “螢幕截圖”)]
然後我們将項目釋出(釋出的時候記得上面提到的裁剪導緻程式集丢失無法使用按需加載的問題,隻要在按需加載的配置中清理掉被裁剪的程式集即可):
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-OLiYCDQt-1676015504455)(https://foruda.gitee.com/images/1675065607571541966/8a38d669_8229157.png “螢幕截圖”)]
然後使用docker compose部署一個nginx代理檢視效果:
建立
docker-compose.yml
檔案,并且添加以下代碼,在
docker-compose.yml
的目前目錄下建立
conf.d
和
wwwroot
倆個檔案夾:
services:
nginx:
image: nginx:stable-alpine
container_name: nginx
volumes:
- ./conf.d:/etc/nginx/conf.d
- ./wwwroot:/wwwroot
ports:
- 811:80
在
conf.d
中建立
webassembly.conf
,并且添加以下代碼:
server {
listen 80;
server_name http://localhost;
location / {
root /wwwroot;
index index.html;
}
}
然後在
docker-compose.yml
所屬目錄中使用
docker-compose up -d
啟動nginx服務
打開浏覽器通路
http://127.0.0.1:811/
(不要使用localhost通路,預設不會啟動壓縮的)然後打開f12調試工具,并且在應用程式中清理掉存儲,在打開網絡選項,重新整理浏覽器,加載完成,優化到了2.3MB,啟動壓縮,并且在釋出的時候裁剪了未使用的程式集:
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-rn2bulz2-1676015504455)(https://foruda.gitee.com/images/1675065598192463678/bcec281c_8229157.png “螢幕截圖”)]
極緻優化 到1MB
在
Demand
項目檔案中添加以下配置,以下配置禁用了一些功能,比如全球化等
<PublishTrimmed>true</PublishTrimmed>
<InvariantGlobalization>true</InvariantGlobalization>
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
<EventSourceSupport>false</EventSourceSupport>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<MetadataUpdaterSupport>false</MetadataUpdaterSupport>
<UseNativeHttpHandler>true</UseNativeHttpHandler>
然後我們繼續上面的操作将其釋出,并且部署到nginx中
在網絡中檢視加載大小,我們看到已經來到了1MB,去掉一些js其實應該更小,這樣它的加載問題得到了很大的解決(來着小夜鲲大佬的建議)
結尾
如果您有更好的優化方案可以聯系我
來着token的分享
demo位址
GitHub
Gitee