├── Samples
└── 01
│ ├── BlazorApp
│ ├── wwwroot
│ │ ├── favicon.ico
│ │ ├── css
│ │ │ ├── open-iconic
│ │ │ │ ├── font
│ │ │ │ │ ├── fonts
│ │ │ │ │ │ ├── open-iconic.eot
│ │ │ │ │ │ ├── open-iconic.otf
│ │ │ │ │ │ ├── open-iconic.ttf
│ │ │ │ │ │ ├── open-iconic.woff
│ │ │ │ │ │ └── open-iconic.svg
│ │ │ │ │ └── css
│ │ │ │ │ │ └── open-iconic-bootstrap.min.css
│ │ │ │ ├── ICON-LICENSE
│ │ │ │ ├── README.md
│ │ │ │ └── FONT-LICENSE
│ │ │ └── app.css
│ │ ├── sample-data
│ │ │ └── weather.json
│ │ └── index.html
│ ├── Pages
│ │ ├── Index.razor
│ │ ├── LazyComponent.razor
│ │ ├── Counter.razor
│ │ └── FetchData.razor
│ ├── _Imports.razor
│ ├── Shared
│ │ ├── MainLayout.razor
│ │ ├── SurveyPrompt.razor
│ │ └── NavMenu.razor
│ ├── App.razor
│ ├── BlazorApp.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ └── AreaAssemblyLazyLoadResolver.cs
│ └── LazyLoadedArea
│ ├── Shared
│ └── DifficultComponent.razor
│ ├── Pages
│ ├── LazyLoadedPage02.razor
│ └── LazyLoadedPage01.razor
│ ├── TimeProvider.cs
│ ├── LazyLoadedArea.csproj
│ ├── MessagesProvider.cs
│ ├── Program.cs
│ └── Properties
│ └── launchSettings.json
├── ReleaseNotes.md
├── src
└── BlazorLazyLoad
│ ├── BlazorLazyLoad.csproj
│ ├── JSInteropMethods.cs
│ ├── IAssemblyLazyLoader.cs
│ ├── AssemblyProvider.cs
│ ├── LazyLoadServicesExtensions.cs
│ ├── AssemblyDependencyResolver.cs
│ ├── LazyLoadComponentPlaceHolder.cs
│ └── RouterLL.cs
├── LICENSE
├── README.md
├── BlazorLazyLoad.sln
└── .gitignore
/Samples/01/BlazorApp/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarekPokornyOva/BlazorLazyLoad/HEAD/Samples/01/BlazorApp/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/Shared/DifficultComponent.razor:
--------------------------------------------------------------------------------
1 | @Title
2 |
3 | @code {
4 | [Parameter]
5 | public string Title { get; set; }
6 | }
7 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Pages/Index.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 |
3 |
Hello, world!
4 |
5 | Welcome to your new app.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarekPokornyOva/BlazorLazyLoad/HEAD/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.eot
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarekPokornyOva/BlazorLazyLoad/HEAD/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.otf
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarekPokornyOva/BlazorLazyLoad/HEAD/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MarekPokornyOva/BlazorLazyLoad/HEAD/Samples/01/BlazorApp/wwwroot/css/open-iconic/font/fonts/open-iconic.woff
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/Pages/LazyLoadedPage02.razor:
--------------------------------------------------------------------------------
1 | @page "/lazyLoaded/page02"
2 |
3 | @inject ITimeProvider _timeProvider
4 |
5 | Lazy load feature works!
6 |
7 | Current time is: @_timeProvider.GetTime()
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/Pages/LazyLoadedPage01.razor:
--------------------------------------------------------------------------------
1 | @page "/lazyLoaded/page01"
2 |
3 | @inject IMessageProvider _messageProvider
4 |
5 | Lazy load feature works!
6 |
7 | Message of the day: @_messageProvider.GetMessage()
8 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Pages/LazyComponent.razor:
--------------------------------------------------------------------------------
1 | @page "/lazycomponent"
2 |
3 | Lazy loaded component
4 |
5 |
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/TimeProvider.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using System;
3 | #endregion using
4 |
5 | namespace LazyLoadedArea
6 | {
7 | public class TimeProvider:ITimeProvider
8 | {
9 | public DateTime GetTime()
10 | => DateTime.Now;
11 | }
12 |
13 | public interface ITimeProvider
14 | {
15 | DateTime GetTime();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Pages/Counter.razor:
--------------------------------------------------------------------------------
1 | @page "/counter"
2 |
3 | Counter
4 |
5 | Current count: @currentCount
6 |
7 |
8 |
9 | @code {
10 | private int currentCount = 0;
11 |
12 | private void IncrementCount()
13 | {
14 | currentCount++;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
7 | @using Microsoft.JSInterop
8 | @using BlazorApp
9 | @using BlazorApp.Shared
10 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
6 |
7 |
8 |
11 |
12 |
13 | @Body
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/App.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Sorry, there's nothing at this address.
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/LazyLoadedArea.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | 3.0
6 | LazyLoaded
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/MessagesProvider.cs:
--------------------------------------------------------------------------------
1 | namespace LazyLoadedArea
2 | {
3 | public class CircuitMessagesProvider:IMessageProvider
4 | {
5 | int _index = 0;
6 | static string[] _messages = new string[] { "That's great assembly lazy load works.", "It's cool the services registration and injection works too!" };
7 |
8 | public string GetMessage()
9 | {
10 | _index++;
11 | if (_index>=_messages.Length)
12 | _index=0;
13 | return _messages[_index];
14 | }
15 | }
16 |
17 | public interface IMessageProvider
18 | {
19 | string GetMessage();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/sample-data/weather.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "date": "2018-05-06",
4 | "temperatureC": 1,
5 | "summary": "Freezing"
6 | },
7 | {
8 | "date": "2018-05-07",
9 | "temperatureC": 14,
10 | "summary": "Bracing"
11 | },
12 | {
13 | "date": "2018-05-08",
14 | "temperatureC": -13,
15 | "summary": "Freezing"
16 | },
17 | {
18 | "date": "2018-05-09",
19 | "temperatureC": -16,
20 | "summary": "Balmy"
21 | },
22 | {
23 | "date": "2018-05-10",
24 | "temperatureC": -2,
25 | "summary": "Chilly"
26 | }
27 | ]
28 |
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/Program.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using Microsoft.Extensions.DependencyInjection;
3 | #endregion using
4 |
5 | namespace LazyLoadedArea
6 | {
7 | public class Program
8 | {
9 | public static void Main(string[] args)
10 | {
11 | //this entrypoint is requested only during project build. Otherwise, it's useless.
12 | }
13 |
14 | public static void ConfigureServices(IServiceCollection services)
15 | {
16 | services.AddSingleton();
17 | services.AddSingleton();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Shared/SurveyPrompt.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
@Title
4 |
5 |
6 | Please take our
7 | brief survey
8 |
9 | and tell us what you think.
10 |
11 |
12 | @code {
13 | // Demonstrates how a parent component can supply parameters
14 | [Parameter]
15 | public string Title { get; set; }
16 | }
17 |
--------------------------------------------------------------------------------
/ReleaseNotes.md:
--------------------------------------------------------------------------------
1 | ### 1.0.0 (2020-05-24)
2 | * AspNetCore.Components version 3.2.0 used.
3 |
4 | ### 0.2.4-3.2.0-20223.4 (2020-05-04)
5 | * AspNetCore.Components version 3.2.0-rc1.20223.4 used.
6 | * Avoid multiple download on LazyLoadComponentPlaceHolder.
7 |
8 | ### 0.2.3-3.2.0-20210.8 (2020-04-21)
9 | * Add lazy loading on component level.
10 |
11 | ### 0.2.2-3.2.0-20210.8 (2020-04-17)
12 | * Downloads gzipped assemblies by default.
13 | * AspNetCore.Components version 3.2.0-preview4.20210.8 used.
14 |
15 | ### 0.2.1-3.2.0-20073.1 (2020-02-17)
16 | * Add referenced assemblies load support.
17 |
18 | ### 0.2-3.2.0-20073.1 (2020-02-15)
19 | * Avoid displaying NotFound fragment during assembly load/init.
20 |
21 | ### 0.1-3.2.0-20073.1 (2020-02-12)
22 | * Initial version
23 |
--------------------------------------------------------------------------------
/Samples/01/LazyLoadedArea/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:54980/",
7 | "sslPort": 44352
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "LazyLoaded": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000"
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/BlazorApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | 3.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Program.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using System;
3 | using System.Net.Http;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
6 | using BlazorLazyLoad;
7 | using Microsoft.Extensions.DependencyInjection;
8 | #endregion using
9 |
10 | namespace BlazorApp
11 | {
12 | public class Program
13 | {
14 | public static async Task Main(string[] args)
15 | {
16 | var builder = WebAssemblyHostBuilder.CreateDefault(args);
17 | builder.RootComponents.Add("app");
18 |
19 | builder.Services.AddTransient(sp => new HttpClient { BaseAddress=new Uri(builder.HostEnvironment.BaseAddress) });
20 | LazyLoadServicesBuilder lazyLoadServicesBuilder = builder.Services.AddLazyLoad();
21 |
22 | WebAssemblyHost host = builder.Build();
23 | lazyLoadServicesBuilder.SetHost(host);
24 | await host.RunAsync();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:54794/",
7 | "sslPort": 44344
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
15 | "environmentVariables": {
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "BlazorApp": {
20 | "commandName": "Project",
21 | "launchBrowser": true,
22 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
23 | "environmentVariables": {
24 | "ASPNETCORE_ENVIRONMENT": "Development"
25 | },
26 | "applicationUrl": "https://localhost:5001;http://localhost:5000"
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/BlazorLazyLoad/BlazorLazyLoad.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1
5 | Blazor LazyLoad
6 | https://github.com/MarekPokornyOva/BlazorLazyLoad
7 | https://github.com/MarekPokornyOva/BlazorLazyLoad
8 | MpSoft
9 | Marek Pokorný
10 | BlazorLazyLoad
11 | BlazorLazyLoad is concept of assembly lazy load in Blazor WASM application.
12 | 1.0.0
13 | LICENSE
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | True
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 MarekPokornyOva
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/css/open-iconic/ICON-LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Waybury
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/src/BlazorLazyLoad/JSInteropMethods.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.JSInterop;
5 | using System;
6 | using System.ComponentModel;
7 | using System.Threading.Tasks;
8 | #endregion using
9 |
10 | namespace BlazorLazyLoad
11 | {
12 | [EditorBrowsable(EditorBrowsableState.Never)]
13 | public static class JSInteropMethods
14 | {
15 | internal static IServiceProvider ServiceProvider;
16 | internal static IServiceCollection Services;
17 | internal static WebAssemblyHost Host;
18 | internal static IRouterEnvelope Router;
19 |
20 | [JSInvokable("NotifyLocationChanged")]
21 | public static async Task NotifyLocationChanged(string uri,bool isInterceptedLink)
22 | {
23 | await ServiceProvider.GetRequiredService().ResolveAsync(uri,isInterceptedLink);
24 |
25 | //Would it be possible to send original parameters and call it via DotNetDispatcher? It'd need to get ServiceProvider.GetRequiredService() instance.
26 | Microsoft.AspNetCore.Components.WebAssembly.Infrastructure.JSInteropMethods.NotifyLocationChanged(uri,isInterceptedLink);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | BlazorApp
8 |
9 |
10 |
11 |
12 |
13 |
14 | Loading...
15 |
16 |
17 | An unhandled error has occurred.
18 |
Reload
19 |
🗙
20 |
21 |
22 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Pages/FetchData.razor:
--------------------------------------------------------------------------------
1 | @page "/fetchdata"
2 | @inject HttpClient Http
3 |
4 | Weather forecast
5 |
6 | This component demonstrates fetching data from the server.
7 |
8 | @if (forecasts == null)
9 | {
10 | Loading...
11 | }
12 | else
13 | {
14 |
15 |
16 |
17 | | Date |
18 | Temp. (C) |
19 | Temp. (F) |
20 | Summary |
21 |
22 |
23 |
24 | @foreach (var forecast in forecasts)
25 | {
26 |
27 | | @forecast.Date.ToShortDateString() |
28 | @forecast.TemperatureC |
29 | @forecast.TemperatureF |
30 | @forecast.Summary |
31 |
32 | }
33 |
34 |
35 | }
36 |
37 | @code {
38 | private WeatherForecast[] forecasts;
39 |
40 | protected override async Task OnInitializedAsync()
41 | {
42 | forecasts = await Http.GetFromJsonAsync("sample-data/weather.json");
43 | }
44 |
45 | public class WeatherForecast
46 | {
47 | public DateTime Date { get; set; }
48 |
49 | public int TemperatureC { get; set; }
50 |
51 | public string Summary { get; set; }
52 |
53 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/Shared/NavMenu.razor:
--------------------------------------------------------------------------------
1 |
7 |
8 |
42 |
43 | @code {
44 | private bool collapseNavMenu = true;
45 |
46 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
47 |
48 | private void ToggleNavMenu()
49 | {
50 | collapseNavMenu = !collapseNavMenu;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/AreaAssemblyLazyLoadResolver.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using BlazorLazyLoad;
3 | using Microsoft.AspNetCore.Components;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Threading.Tasks;
9 | #endregion using
10 |
11 | namespace BlazorApp
12 | {
13 | public class AreaAssemblyLazyLoadResolver:AssemblyLazyLoadResolverBase
14 | {
15 | readonly IAssemblyDependencyResolver _assemblyDependencyResolver;
16 | public AreaAssemblyLazyLoadResolver(IAssemblyDependencyResolver assemblyDependencyResolver)
17 | {
18 | _assemblyDependencyResolver=assemblyDependencyResolver;
19 | }
20 |
21 | public override async Task ResolveAsync(string uri,bool isInterceptedLink)
22 | {
23 | //Get requested assembly based on the first path segment. This is highly specific, other applications might use different strategy.
24 | string[] segments = new Uri(uri,UriKind.Absolute).Segments.Select(x => x.Trim('/')).Where(x => x.Length>0).ToArray();
25 | if (segments.Length<2)
26 | return;
27 | string assemblyName = segments[0];
28 |
29 | //We need to inject new assembly to the router because it resolves which page to display.
30 | IRouterEnvelope router = base.Router;
31 |
32 | IEnumerable additionalAssemblies = router.AdditionalAssemblies??Enumerable.Empty();
33 | //Don't inject the assembly multiple times.
34 | if (additionalAssemblies.Any(x => string.Equals(x.GetName().Name,assemblyName,StringComparison.OrdinalIgnoreCase)))
35 | return;
36 |
37 | //Load assembly including its dependencies
38 | IEnumerable newAssemblies = await _assemblyDependencyResolver.ResolveAsync(assemblyName);
39 | if (!newAssemblies.Any())
40 | return;
41 |
42 | //Register also services
43 | foreach (Assembly asm in newAssemblies)
44 | LoadServices(asm);
45 |
46 | //Inject the assembly to the router.
47 | ParameterView pv = ParameterView.FromDictionary(router.GetType().GetProperties()
48 | .Where(x => x.CustomAttributes.Any(x => x.AttributeType==typeof(ParameterAttribute)))
49 | .ToDictionary(pi => pi.Name,pi => string.Equals(pi.Name,nameof(IRouterEnvelope.AdditionalAssemblies),StringComparison.Ordinal)
50 | ? additionalAssemblies.Concat(newAssemblies).ToArray()
51 | : pi.GetValue(router)));
52 | await router.SetParametersAsync(pv);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BlazorLazyLoad
2 |
3 | [](https://www.nuget.org/packages/BlazorLazyLoad)
4 | [](https://www.nuget.org/packages/BlazorLazyLoad)
5 | [](https://github.com/MarekPokornyOva/BlazorLazyLoad/blob/master/LICENSE)
6 |
7 | ### Description
8 | BlazorLazyLoad is mostly loudly shared idea/concept of assembly lazy load in Blazor WASM application.
9 | Splitting an application speeds up its start and also saves network traffic.
10 |
11 | ### Features
12 | - Lazy loads assemblies.
13 | - Lazy resolving on both page and component level.
14 | - Registers included pages for routing.
15 | - Registers included services to ServiceProvider.
16 |
17 | ### Usage
18 | 1) Include Nuget package - https://www.nuget.org/packages/BlazorLazyLoad/ in Blazor WASM application's project.
19 | 2) Create custom assembly lazy load handler. The \Samples\01\BlazorApp\AreaAssemblyLazyLoadResolver.cs might be good example - it expects areas split strategy. The resolver can also register the services defined in the lazy loaded assembly.
20 | 3) Change Router in App.razor to the enhanced one. That invokes AssemblyLazyLoadResolver when a lazy loaded page is navigated at first (entered to navigation bar or on the page refresh).
21 | 4) Register services - call builder.Services.AddLazyLoad(); within Program.Main() method.
22 | 5) Redirect navigation event to custom handler - see \Samples\01\BlazorApp\wwwroot\index.html.
23 | 6) Create project containing lazy loaded pages - see \Samples\01\LazyLoadedArea.
24 | 7) The built assembly has to be copied from wwwroot\\_framework\\_bin to the main project's wwwroot\\_framework\\bin folder. It's recommended to use gzipped versions.
25 | 8) See \Samples\01\BlazorApp\Pages\LazyComponent for component level lazy loading.
26 |
27 | ### Notes
28 | - All is provided as is without any warranty.
29 | - The target of this concept has been "make it functional for any price". Therefore some pieces are bit "hacky".
30 | - Developed with version 3.2.0 wasm.
31 |
32 | ### Release notes
33 | [See](./ReleaseNotes.md)
34 |
35 | ### Thanks to Blazor team members for their work
36 | ### Thanks to Chris Sainty for his article https://chrissainty.com/an-in-depth-look-at-routing-in-blazor/
37 |
--------------------------------------------------------------------------------
/src/BlazorLazyLoad/IAssemblyLazyLoader.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
3 | using Microsoft.AspNetCore.Components.RenderTree;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using System;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Runtime.CompilerServices;
9 | using System.Threading.Tasks;
10 | #endregion using
11 |
12 | namespace BlazorLazyLoad
13 | {
14 | public interface IAssemblyLazyLoadResolver
15 | {
16 | Task ResolveAsync(string uri,bool isInterceptedLink);
17 | }
18 |
19 | public abstract class AssemblyLazyLoadResolverBase:IAssemblyLazyLoadResolver
20 | {
21 | public abstract Task ResolveAsync(string uri,bool isInterceptedLink);
22 |
23 | protected IRouterEnvelope Router => JSInteropMethods.Router;
24 |
25 | public static void LoadServices(Assembly assembly)
26 | {
27 | //Find Program.ConfigureServices(IServiceCollection) method.
28 | MethodInfo configureServices = FindDefaultConfigureServicesMethod(assembly);
29 | if (configureServices==default)
30 | return;
31 |
32 | LoadServices(configureServices);
33 | }
34 |
35 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
36 | static MethodInfo FindDefaultConfigureServicesMethod(Assembly assembly)
37 | => assembly.GetTypes().FirstOrDefault(x => string.Equals(x.Name,"Program",StringComparison.Ordinal))?
38 | .GetMethod("ConfigureServices",BindingFlags.Public|BindingFlags.Static,null,new Type[] { typeof(IServiceCollection) },null);
39 |
40 | public static void LoadServices(MethodInfo configureServices)
41 | {
42 | //Call the ConfigureServices(IServiceCollection) method.
43 | IServiceCollection services = JSInteropMethods.Services;
44 | configureServices.Invoke(null,new object[] { services });
45 |
46 | //Is it possible to get if a service registration was changed? Maybe with tracking wrapper. But how about specific Factory on transient ServiceDescriptor?
47 | //We can return from this method if nothing got changed.
48 |
49 | //Get new IServiceProvider and inject it to Renderer and WebAssemblyHost.
50 | IServiceProvider sp = services.BuildServiceProvider();
51 | IServiceScope newScope = sp.GetRequiredService().CreateScope();
52 | WebAssemblyHost host = JSInteropMethods.Host;
53 | Type hostType = host.GetType();
54 | hostType.GetField("_scope",BindingFlags.NonPublic|BindingFlags.Instance).SetValue(host,newScope);
55 | object renderer = hostType.GetField("_renderer",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(host);
56 | typeof(Renderer).GetField("_serviceProvider",BindingFlags.NonPublic|BindingFlags.Instance).SetValue(renderer,newScope.ServiceProvider);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/BlazorLazyLoad/AssemblyProvider.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Net.Http;
5 | using System.Threading.Tasks;
6 | #endregion using
7 |
8 | namespace BlazorLazyLoad
9 | {
10 | public interface IAssemblyProvider
11 | {
12 | Task<(byte[] DllBytes, byte[] PdbBytes)> GetAssemblyAsync(string assemblyName);
13 | }
14 |
15 | #region AssemblyProviderBase
16 | public abstract class AssemblyProviderBase:IAssemblyProvider
17 | {
18 | readonly HttpClient _httpClient;
19 | public AssemblyProviderBase(HttpClient httpClient)
20 | => _httpClient=httpClient;
21 |
22 | public abstract Task<(byte[] DllBytes, byte[] PdbBytes)> GetAssemblyAsync(string assemblyName);
23 |
24 | public Task DownloadFileBytes(string filename)
25 | => _httpClient.GetByteArrayAsync("_framework/_bin/"+filename);
26 |
27 | public async Task TryDownloadFileBytes(string filename)
28 | {
29 | try
30 | {
31 | return await DownloadFileBytes(filename);
32 | }
33 | catch
34 | { }
35 | return null;
36 | }
37 | }
38 | #endregion AssemblyProviderBase
39 |
40 | public class DefaultAssemblyDownloader:AssemblyProviderBase
41 | {
42 | public DefaultAssemblyDownloader(HttpClient httpClient) : base(httpClient)
43 | { }
44 |
45 | public override async Task<(byte[] DllBytes, byte[] PdbBytes)> GetAssemblyAsync(string assemblyName)
46 | {
47 | //Ungzip works with streams but it's still worth to download data as byte[] because HttpClient do various memory optimizations and content length checks when downloading as byte[].
48 | Task dllBytes = Ungzip(DownloadFileBytes(assemblyName+".dll.gz"));
49 | return (await dllBytes, await Ungzip(TryDownloadFileBytes(assemblyName+".pdb.gz")));
50 | }
51 |
52 | async Task Ungzip(Task source)
53 | {
54 | byte[] gzipBytes = await source;
55 | if (gzipBytes==null)
56 | return null;
57 | using (Stream gzipStream = new MemoryStream(gzipBytes))
58 | using (GZipStream decompressStream = new GZipStream(gzipStream,CompressionMode.Decompress))
59 | using (MemoryStream rawStream = new MemoryStream())
60 | {
61 | decompressStream.CopyTo(rawStream);
62 | return rawStream.GetBuffer();
63 | }
64 | }
65 | }
66 |
67 | public class NonGZippedAssemblyDownloader:AssemblyProviderBase
68 | {
69 | public NonGZippedAssemblyDownloader(HttpClient httpClient) : base(httpClient)
70 | { }
71 |
72 | public override async Task<(byte[] DllBytes, byte[] PdbBytes)> GetAssemblyAsync(string assemblyName)
73 | {
74 | Task dllBytes = DownloadFileBytes(assemblyName+".dll");
75 | return (await dllBytes, await TryDownloadFileBytes(assemblyName+".pdb"));
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/BlazorLazyLoad/LazyLoadServicesExtensions.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using System;
5 | #endregion using
6 |
7 | namespace BlazorLazyLoad
8 | {
9 | public static class LazyLoadServicesExtensions
10 | {
11 | public static LazyLoadServicesBuilder AddLazyLoadCore(this IServiceCollection services)
12 | {
13 | JSInteropMethods.Services=services;
14 | JSInteropMethods.ServiceProvider=services.BuildServiceProvider();
15 | return new LazyLoadServicesBuilder(services);
16 | }
17 |
18 | public static LazyLoadServicesBuilder AddLazyLoad(this IServiceCollection services) where TAssemblyLazyLoadResolver : class, IAssemblyLazyLoadResolver
19 | => AddLazyLoad(services);
20 |
21 | public static LazyLoadServicesBuilder AddLazyLoad(this IServiceCollection services)
22 | where TAssemblyLazyLoadResolver : class, IAssemblyLazyLoadResolver
23 | where TAssemblyDependencyResolver : class, IAssemblyDependencyResolver
24 | where TAssemblyProvider : class, IAssemblyProvider
25 | {
26 | services.AddSingleton();
27 | services.AddSingleton();
28 | services.AddSingleton();
29 | return services.AddLazyLoadCore();
30 | }
31 |
32 | public static LazyLoadServicesBuilder AddLazyLoad(this IServiceCollection services,
33 | Func assemblyLazyLoadResolverImplementationFactory,
34 | Func assemblyDependencyResolverImplementationFactory,
35 | Func assemblyProviderImplementationFactory
36 | )
37 | where TAssemblyLazyLoadResolver : class, IAssemblyLazyLoadResolver
38 | where TAssemblyDependencyResolver : class, IAssemblyDependencyResolver
39 | where TAssemblyProvider : class, IAssemblyProvider
40 | {
41 | services.AddSingleton(assemblyLazyLoadResolverImplementationFactory);
42 | services.AddSingleton(assemblyDependencyResolverImplementationFactory);
43 | services.AddSingleton(assemblyProviderImplementationFactory);
44 | return services.AddLazyLoadCore();
45 | }
46 | }
47 |
48 | public class LazyLoadServicesBuilder
49 | {
50 | internal LazyLoadServicesBuilder(IServiceCollection services)
51 | => Services=services;
52 |
53 | public void SetHost(WebAssemblyHost host)
54 | {
55 | JSInteropMethods.Host=host;
56 | }
57 |
58 | public IServiceCollection Services { get; }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/BlazorLazyLoad/AssemblyDependencyResolver.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.Immutable;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Reflection.Metadata;
8 | using System.Reflection.PortableExecutable;
9 | using System.Threading.Tasks;
10 | #endregion using
11 |
12 | namespace BlazorLazyLoad
13 | {
14 | public interface IAssemblyDependencyResolver
15 | {
16 | Task> ResolveAsync(string assemblyName);
17 | }
18 |
19 | public class MetadataAssemblyDependencyResolver:IAssemblyDependencyResolver
20 | {
21 | static AssemblyNameComparer _assemblyNameComparer = new AssemblyNameComparer();
22 | readonly IAssemblyProvider _assemblyProvider;
23 |
24 | public MetadataAssemblyDependencyResolver(IAssemblyProvider assemblyProvider)
25 | => _assemblyProvider=assemblyProvider;
26 |
27 | public async Task> ResolveAsync(string assemblyName)
28 | {
29 | Dictionary assemblies = new Dictionary(_assemblyNameComparer);
30 | await ProcessAssemblyNameAsync(assemblyName,assemblies);
31 |
32 | //AppDomain.CurrentDomain.AssemblyResolve+=(sender,args) => { return null; args. };
33 | return assemblies.Select(x => { (byte[] dllBytes, byte[] pdbBytes)=x.Value; Assembly asm = pdbBytes==null ? Assembly.Load(dllBytes) : Assembly.Load(dllBytes,pdbBytes); return asm; }).ToArray();
34 | }
35 |
36 | async Task ProcessAssemblyNameAsync(string assemblyName,Dictionary assemblies)
37 | {
38 | //Get assembly
39 | (byte[] DllBytes, byte[] PdbBytes) bytes = await _assemblyProvider.GetAssemblyAsync(assemblyName);
40 | assemblies[assemblyName]=bytes;
41 |
42 | //Resolve referenced assemblies
43 | string[] assemblyReferences;
44 | using (PEReader pEReader = new PEReader(ImmutableArray.Create(bytes.DllBytes)))
45 | {
46 | MetadataReader mdReader = pEReader.GetMetadataReader(MetadataReaderOptions.None);
47 | assemblyReferences=mdReader.AssemblyReferences.Select(x => mdReader.GetAssemblyReference(x).GetAssemblyName().Name).ToArray();
48 | }
49 |
50 | //Filter out referenced assemblies already managed or loaded
51 | lock (assemblies)
52 | {
53 | assemblyReferences=assemblyReferences.Where(x => !assemblies.ContainsKey(x))
54 | .Except(AppDomain.CurrentDomain.GetAssemblies().Select(x => x.GetName().Name),_assemblyNameComparer)
55 | .ToArray();
56 | foreach (string item in assemblyReferences)
57 | assemblies.Add(item,default);
58 | }
59 |
60 | //Find deeper references
61 | await Task.WhenAll(assemblyReferences.Select(x => ProcessAssemblyNameAsync(x,assemblies)));
62 | }
63 |
64 | class AssemblyNameComparer:IEqualityComparer
65 | {
66 | public bool Equals(string x,string y)
67 | => string.Equals(x,y,StringComparison.OrdinalIgnoreCase);
68 |
69 | public int GetHashCode(string obj)
70 | => 0;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/BlazorLazyLoad/LazyLoadComponentPlaceHolder.cs:
--------------------------------------------------------------------------------
1 | #region using
2 | using Microsoft.AspNetCore.Components;
3 | using Microsoft.AspNetCore.Components.Rendering;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Threading.Tasks;
9 | #endregion using
10 |
11 | namespace BlazorLazyLoad
12 | {
13 | public class LazyLoadComponentPlaceHolder:ComponentBase
14 | {
15 | Type _innerComponentType;
16 | KeyValuePair[] _parms;
17 |
18 | [Inject] IAssemblyDependencyResolver AssemblyDependencyResolver { get; set; }
19 |
20 | public async override Task SetParametersAsync(ParameterView parameters)
21 | {
22 | IReadOnlyDictionary parms=parameters.ToDictionary();
23 |
24 | if (!(parms.TryGetValue("_Assembly",out object asmVal)&&(asmVal is string asm)&&parms.TryGetValue("_Type",out object typeVal)&&typeVal is string type))
25 | throw new ArgumentException("Specify _Assembly and _Type attributes as string values identifying a valid component class.");
26 | _parms=parms.Where(x => string.Equals(x.Key,"_Assembly",StringComparison.OrdinalIgnoreCase)==string.Equals(x.Key,"_Type",StringComparison.OrdinalIgnoreCase)).ToArray();
27 |
28 | _innerComponentType=await EnsureComponentAsync(asm,type);
29 | if (_innerComponentType==null)
30 | throw new ArgumentException($"Assembly {asm} can't be resolved.");
31 |
32 | StateHasChanged();
33 | }
34 |
35 | protected override void BuildRenderTree(RenderTreeBuilder builder)
36 | {
37 | builder.OpenComponent(1,_innerComponentType);
38 | int a = 1;
39 | foreach (KeyValuePair parm in _parms)
40 | builder.AddAttribute(++a,parm.Key,parm.Value);
41 | builder.CloseComponent();
42 | }
43 |
44 | async Task EnsureComponentAsync(string assemblyName,string type)
45 | {
46 | //We need to inject new assembly to the router because it resolves which page to display.
47 | IRouterEnvelope router = JSInteropMethods.Router;
48 | IEnumerable additionalAssemblies = router.AdditionalAssemblies??Enumerable.Empty();
49 |
50 | //Don't inject the assembly multiple times.
51 | Assembly asm = additionalAssemblies.FirstOrDefault(x => string.Equals(x.GetName().Name,assemblyName,StringComparison.OrdinalIgnoreCase));
52 | if (asm==default)
53 | {
54 | //Load assembly including its dependencies
55 | IEnumerable newAssemblies = await AssemblyDependencyResolver.ResolveAsync(assemblyName);
56 | if (!newAssemblies.Any())
57 | return null;
58 |
59 | LoadServices(newAssemblies);
60 |
61 | //Inject the assembly to the router.
62 | ParameterView pv = ParameterView.FromDictionary(new Dictionary() { { nameof(IRouterEnvelope.AdditionalAssemblies),additionalAssemblies.Concat(newAssemblies).ToArray() } });
63 | await router.SetParametersAsync(pv);
64 |
65 | asm=newAssemblies.First();
66 | }
67 |
68 | return asm.GetType(type,true);
69 | }
70 |
71 | protected virtual void LoadServices(IEnumerable newAssemblies)
72 | {
73 | foreach (Assembly asm in newAssemblies)
74 | AssemblyLazyLoadResolverBase.LoadServices(asm);
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/BlazorLazyLoad.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29709.97
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorApp", "Samples\01\BlazorApp\BlazorApp.csproj", "{DA6694D6-2F03-43F1-89B3-A4E79E99E4C8}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{FD7A9FE6-95E9-498C-B9B2-3153234A32FF}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "01", "01", "{4B1FB6EE-3025-4A3A-A6C9-00CA17E2A2F3}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{28E3785A-B870-4B10-93F2-3C555BBBA481}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LazyLoadedArea", "Samples\01\LazyLoadedArea\LazyLoadedArea.csproj", "{1E88354F-A7FF-4E76-A158-DA248DE09D30}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{44E9EAAF-A5F8-409E-9A17-B25A9E69D579}"
17 | ProjectSection(SolutionItems) = preProject
18 | LICENSE = LICENSE
19 | README.md = README.md
20 | ReleaseNotes.md = ReleaseNotes.md
21 | EndProjectSection
22 | EndProject
23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorLazyLoad", "src\BlazorLazyLoad\BlazorLazyLoad.csproj", "{7CBE2DB8-07C6-494A-91E4-0B1D2F638F37}"
24 | EndProject
25 | Global
26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
27 | Debug|Any CPU = Debug|Any CPU
28 | Release|Any CPU = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
31 | {DA6694D6-2F03-43F1-89B3-A4E79E99E4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {DA6694D6-2F03-43F1-89B3-A4E79E99E4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {DA6694D6-2F03-43F1-89B3-A4E79E99E4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {DA6694D6-2F03-43F1-89B3-A4E79E99E4C8}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {1E88354F-A7FF-4E76-A158-DA248DE09D30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36 | {1E88354F-A7FF-4E76-A158-DA248DE09D30}.Debug|Any CPU.Build.0 = Debug|Any CPU
37 | {1E88354F-A7FF-4E76-A158-DA248DE09D30}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {1E88354F-A7FF-4E76-A158-DA248DE09D30}.Release|Any CPU.Build.0 = Release|Any CPU
39 | {7CBE2DB8-07C6-494A-91E4-0B1D2F638F37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40 | {7CBE2DB8-07C6-494A-91E4-0B1D2F638F37}.Debug|Any CPU.Build.0 = Debug|Any CPU
41 | {7CBE2DB8-07C6-494A-91E4-0B1D2F638F37}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 | {7CBE2DB8-07C6-494A-91E4-0B1D2F638F37}.Release|Any CPU.Build.0 = Release|Any CPU
43 | EndGlobalSection
44 | GlobalSection(SolutionProperties) = preSolution
45 | HideSolutionNode = FALSE
46 | EndGlobalSection
47 | GlobalSection(NestedProjects) = preSolution
48 | {DA6694D6-2F03-43F1-89B3-A4E79E99E4C8} = {4B1FB6EE-3025-4A3A-A6C9-00CA17E2A2F3}
49 | {4B1FB6EE-3025-4A3A-A6C9-00CA17E2A2F3} = {FD7A9FE6-95E9-498C-B9B2-3153234A32FF}
50 | {1E88354F-A7FF-4E76-A158-DA248DE09D30} = {4B1FB6EE-3025-4A3A-A6C9-00CA17E2A2F3}
51 | {7CBE2DB8-07C6-494A-91E4-0B1D2F638F37} = {28E3785A-B870-4B10-93F2-3C555BBBA481}
52 | EndGlobalSection
53 | GlobalSection(ExtensibilityGlobals) = postSolution
54 | SolutionGuid = {C305E47F-4B15-40FF-8444-B0C0ACF7F72B}
55 | EndGlobalSection
56 | EndGlobal
57 |
--------------------------------------------------------------------------------
/Samples/01/BlazorApp/wwwroot/css/open-iconic/README.md:
--------------------------------------------------------------------------------
1 | [Open Iconic v1.1.1](http://useiconic.com/open)
2 | ===========
3 |
4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons)
5 |
6 |
7 |
8 | ## What's in Open Iconic?
9 |
10 | * 223 icons designed to be legible down to 8 pixels
11 | * Super-light SVG files - 61.8 for the entire set
12 | * SVG sprite—the modern replacement for icon fonts
13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats
14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats
15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px.
16 |
17 |
18 | ## Getting Started
19 |
20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections.
21 |
22 | ### General Usage
23 |
24 | #### Using Open Iconic's SVGs
25 |
26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute).
27 |
28 | ```
29 |
30 | ```
31 |
32 | #### Using Open Iconic's SVG Sprite
33 |
34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack.
35 |
36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `