Skip to content

Versionable Wiki #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 9, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
versionable wiki
  • Loading branch information
isc30 committed May 9, 2020
commit e7a588c6ac8fe91d57307a5a3103f36a8cb6194e
8 changes: 8 additions & 0 deletions .github/workflows/build-all.yml
Original file line number Diff line number Diff line change
@@ -49,6 +49,14 @@ jobs:
- name: Test demo
run: dotnet test demo/Demo.sln -c Release

- name: Deploy wiki
uses: jamesives/[email protected]
with:
FOLDER: docs
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
REPOSITORY_NAME: isc30/blazor-lazy-loading.wiki
BRANCH: master

- name: Upload artifacts
uses: actions/upload-artifact@v1
with:
8 changes: 8 additions & 0 deletions .github/workflows/nuget-publish.yml
Original file line number Diff line number Diff line change
@@ -69,6 +69,14 @@ jobs:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
BRANCH: gh-pages

- name: Deploy wiki
uses: jamesives/[email protected]
with:
FOLDER: docs
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
REPOSITORY_NAME: isc30/blazor-lazy-loading.wiki
BRANCH: master

- name: Upload artifacts
uses: actions/upload-artifact@v1
with:
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center">
<a href="https://github.com/isc30/blazor-lazy-loading">
<img src="doc/img/logo.png?raw=true" />
<img src="docs/img/logo.png?raw=true" />
</a>
</p>
<p align="center">
@@ -13,6 +13,6 @@

Automatic Lazy Loading support for Blazor WebAssembly and Blazor Server!<br/><br/>

[![Documentation / Wiki](doc/img/documentation.png?raw=true)](https://github.com/isc30/blazor-lazy-loading/wiki)
[![Documentation / Wiki](docs/img/documentation.png?raw=true)](https://github.com/isc30/blazor-lazy-loading/wiki)

[![Demo](doc/img/demo.png?raw=true)](https://isc30.github.io/blazor-lazy-loading/)
[![Demo](docs/img/demo.png?raw=true)](https://isc30.github.io/blazor-lazy-loading/)
115 changes: 115 additions & 0 deletions docs/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# &lt;Lazy /&gt;

Renders a Component (`IComponent`) from a Lazy Module based on it's **LazyName** or **TypeFullName** (*string*).

This Component needs to exist in a *known* Lazy Module, either [hinted directly](Configuring-Lazy-Loading-@-Server#modulehints) or using [ModulesHost strategy](Creating-a-Lazy-Loadable-Module#creating-an-aggregated-module).

# Render by using TypeFullName

The is no extra prerequisite to render an `IComponent` by using **TypeFullName**:

```razor
<Lazy Name="MyApplication.Hello" />
```

This snippet renders a `Hello` Component assuming the type is `MyApplication.Hello`. It will **automatically fetch** the manifests, consume them to **locate** the Component and **download** the minimum assemblies to make it work 😄

# Named Components

It is possible to **manually name** your Components and use that name later to resolve them.<br />
This is done by adding a simple **attribute** `BlazorLazyLoading.LazyNameAttribute` to your Component:

* Syntax: `.razor`
```razor
@attribute [LazyName("SayHello")]
<h1>Hello!</h3>
```
* Syntax: `.cs`
```cs
[LazyName("SayHello")]
public class Hello : IComponent { ... }
```
And then **render** it like the following:
```razor
<Lazy Name="SayHello" />
```

In order to *debug* if your component **name** is generated properly, you can check the contents of `_lazy.json`.
```json
{
"MyApplication": {
"Components": [
{
"TypeFullName": "MyApplication.Hello",
"Name": "SayHello"
}
]
}
}
```

# Custom 'Loading' view

It is possible to customize the `<Lazy>` Component with a **loading** `RenderFragment` that will get rendered while everything is being initialized.

It only requires setting a `RenderFragment` called `Loading`:

```razor
<Lazy Name="SayHello">
<Loading>
<p>Loading, please wait</p>
<div class="spinner" />
</Loading>
</Lazy>
```

# Custom 'Error' view

It is possible to customize the 'error' `RenderFragment` that gets renderd if the Component fails to initialize *(because some assembly failed to load, such element doesn't exist, etc)*.

It only requires setting a `RenderFragment` called `Error`:

```razor
<Lazy Name="SayHello">
<Error>
<p class="error">Something bad happened</p>
</Error>
</Lazy>
```

# Handling Errors

It is possible to specify if a Component is **Required** (if a loading error throws an exception) by setting the property `Required` *(bool)* on it.

```razor
<Lazy Required="false" Name="Unknown" /> @* this will simply not get rendered *@
<Lazy Required="true" Name="Unknown" /> @* this will throw an exception *@
@* both will render the 'error' view *@
```

# Hooking into Events

There are 2 Events you can hook to by just setting Callbacks in the Component:

* #### OnBeforeLoadAsync

This callback will be awaited **before** trying to resolve the Component from the manifests.
Useful for delaying a Component render to perform another operation before, and Debugging with `Task.Delay`.

* #### OnAfterLoad

This callback will be invoked **after** resolving and rendering the lazy Component.

```razor
<Lazy
Name="SayHello"
OnBeforeLoadAsync="l => Task.Delay(500)"
OnAfterLoad='l => Console.WriteLine($"Loaded LazyComponent: {l.Name}")' />
```

# SEO and Prerendering Support

Don't worry about SEO. If you use BlazorServer or Prerendering, the Component HTML will be **fully rendered** automatically by the server so there is no difference between rendering it directly and using `<Lazy>`.
70 changes: 70 additions & 0 deletions docs/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# &lt;LazyRouter /&gt;

Provides SPA navigation for Pages and Routes in Lazy Modules. It is a direct replacement of `<Router>`.

The pages need to exist in a *known* Lazy Module, either [hinted directly](Configuring-Lazy-Loading-@-Server#modulehints) or using [ModulesHost strategy](Creating-a-Lazy-Loadable-Module#creating-an-aggregated-module).

# Creating Lazy Pages and Routes

Pages and Routes don't require any configuration, just create them as you would normally (using `@page` attribute, etc).

If the Page is contained in a **Module**, the manifest *(_lazy.json)* will already generate all the information for routing automatically:

```json
{
"MyApplication": {
"Routes": [
{
"Route": "/counter",
"Name": "CounterPage"
}
]
}
}
```

# Using LazyRouter

As mentioned before, this is a **direct replacement** of `Router` so it's safe to replace one with another in your `App.razor` file.

```diff
- <Router AppAssembly="@typeof(Program).Assembly">
+ <LazyRouter AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<LayoutView Layout="@typeof(MainLayout)">
<RouteView RouteData="@routeData" />
</LayoutView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address</p>
</LayoutView>
</NotFound>
- </Router>
+ </LazyRouter>
```

Done! The router will automatically provide SPA style navigation for your lazy routes!

# Custom 'Loading' screen

It is possible to customize the 'loading' screen while a Page is downloading the needed Assemblies.

This is done by setting the `Loading` `RenderFragment`:

```razor
<LazyRouter AppAssembly="@typeof(Program).Assembly">
<Loading Context="moduleName">
<LayoutView Layout="@typeof(MainLayout)">
<div class="fullScreenOverlay">
<LoadingSpinner />
</div>
</LayoutView>
</Loading>
@* ... the rest of <Found> and <NotFound> *@
</LazyRouter>
```

# SEO and Prerendering Support

There is no need to worry about SEO. If you use BlazorServer or Prerendering, the returned page HTML will be **fully rendered** automatically by the server so there is no difference between the prerendered HTML content of `<Router>` and `<LazyRouter>`.
9 changes: 9 additions & 0 deletions docs/Components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
There are multiple ways of using resources from modules, but the simplest one is to use the built-in components from `BlazorLazyLoading.Components`.

It should be installed in every library that wants to consume decoupled resources:

```xml
<PackageReference Include="BlazorLazyLoading.Components" Version="1.1.0" />
```

This section will cover the different components available and their usage.
47 changes: 47 additions & 0 deletions docs/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
The following nuget package includes everything you need to support lazy loading on **Blazor Server** and **Prerendering**.<br/>

It can be installed by adding the following line inside the host csproj:

```xml
<PackageReference Include="BlazorLazyLoading.Server" Version="1.1.0" PrivateAssets="all" />
```

It will also require to be initialized from **Startup.cs** by adding the following lines:<br/>

```cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddLazyLoading(new LazyLoadingOptions
{
ModuleHints = new[] { "ModulesHost" }
});
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ideally after calling app.UseStaticFiles()
app.UseLazyLoading(); // serves DLL and PDB files as octet/stream
}
}
```
# LazyLoadingOptions

* #### ModuleHints

In order to find the `_lazy.json` manifest files and DLLs, you need to specify *at least* an entry-point to a lazy module. This **must** be done by passing the *"known modules"* as **string**.

>Specifies a list of Module Names (hints) to:
>- Download DLLs from them
>- Use their manifest to locate lazy resources
* #### UseAssemblyIsolation

Serverside Blazor has a small disadvantage: by default, the loaded assemblies are in **the same context** for every user. If you have a `static` field in them, the value will be shared accross all SignalR connections. If this happens, it can introduce weird bugs and massive scalability issues (*it could also happen with a nuget package using a static field internally*).

To avoid these issues, BlazorLazyLoading introduces full assembly isolation by creating a Scoped `AssemblyLoadContext`. Unless you really know what you are doing, it is recommended to **NOT** turn this off.

>default: true
>Configures assembly isolation level. Do NOT set this to 'false' unless you want to share 'static' fields between users.<br />
>Keeping this enabled ensures that the server can be scaled horizontally.<br />
58 changes: 58 additions & 0 deletions docs/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
The following nuget package includes everything you need to support lazy loading on **Blazor WebAssembly** (when statically published, using DevServer or using [DualMode](https://github.com/Suchiman/BlazorDualMode)).<br/>

It can be installed by adding the following line inside the host csproj:

```xml
<PackageReference Include="BlazorLazyLoading.Wasm" Version="1.1.0" PrivateAssets="all" />
```

It will also require to be initialized from **Program.cs** by adding the following lines:<br/>

```cs
builder.Services.AddLazyLoading(new LazyLoadingOptions
{
ModuleHints = new[] { "ModulesHost" }
});
```

# Configuring the Linker

In order to load assemblies dynamically, the linker can be a big issue since it is **enabled** for release builds **by default**.

There are 2 ways of approaching this:

### Disabling the Linker (easy)

This option sounds worse that it is in reality. Yes, disabling the linker will ship mscorlib as initial download to the browser, **BUT that's all**. The initial impact won't be big if we Lazy Load the rest of the application.

```xml
<PropertyGroup>
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
</PropertyGroup>
```

### Using LinkerConfig.xml (advanced)

Even if this solution is more advanced, it will give us the best performance. Please refer to [the following guide](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/configure-linker?view=aspnetcore-3.1#control-linking-with-a-configuration-file) to setup your *LinkerConfig.xml*.

The recommendation here would be something like this, but you might need to adapt it for your needs:

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<linker>
<assembly fullname="netstandard" /> <!-- keep full netstandard since its used by the lazy modules -->
<assembly fullname="ModulesHost" /> <!-- keep full entrypoint -->
</linker>
```

Done! Lazy Loading is now configured 😄

# LazyLoadingOptions

* #### ModuleHints

In order to find the `_lazy.json` manifest files and DLLs, you need to specify *at least* an entry-point to a lazy module. This **must** be done by passing the *"known modules"* as **string**.

>Specifies a list of Module Names (hints) to:
>- Download DLLs from them
>- Use their manifest to locate lazy resources
5 changes: 5 additions & 0 deletions docs/Configuring-Lazy-Loading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Depending on the Blazor host, different instructions are required.<br/>
Please refer to the specific guide 😄

- [WebAssembly](Configuring-Lazy-Loading-@-WebAssembly)
- [BlazorServer](Configuring-Lazy-Loading-@-Server)
75 changes: 75 additions & 0 deletions docs/Creating-a-Lazy-Loadable-Module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
A lazy loadable module is just a **.NET assembly (project)** with few extra steps at build time.

In order to covert your csproj to a lazy loadable module, it will require:
- To use `Microsoft.NET.Sdk.Razor` as SDK *(RazorLib project)*.

After checking that, just add:
```xml
<PackageReference Include="BlazorLazyLoading.Module" Version="1.1.0" PrivateAssets="all" />
```

and... **done!** This project is now a module and can be lazy loaded 😄

> ℹ️ `BlazorLazyLoading.Module` will **NOT** add any runtime dependency, it just contains build tooling.
# Building the Module

When building the module, you will notice how 2 *paths* are automatically generated.<br/>
Their content is very useful when debugging 😄

- **wwwroot/_lazy.json** *(manifest file)*

This file includes metadata to be consumed by the client in order to effectively find resources inside the Module.<br/>As for v1 it includes **Razor Components and Pages** (with routing).


- **wwwroot/_lazy/** *(bin directory)*

This folder will contain every runtime dependency (DLL) that this Module needs. It is required to support loading NuGet and project references effectively by Blazor.

These files will be automaticlly served as StaticWebAssets by the Blazor host.

# 🌟 Using "ModulesHost" (aggregated module)

Having *every* dependency copied to each module isn't a storage-friendly solution but it's perfect for creating completely isolated modules. The "aggregated" module approach solves the storage and discoverability issue by completely isolating the assemblies from BlazorLazyLoading, except for a single one (`ModulesHost`) that will aggregate and serve all the lazy DLLs.

This approach is **always recommended** if:

- You are **NOT** aiming for **absolute dependency isolation** and **just want lazy loading**.
- You are **migrating** an **already existing application** to use BlazorLazyLoading.

Steps:

1. Remove the `BlazorLazyLoading.Module` reference from all your Lazy Modules (if you already converted them).

1. Create a new Module project **without any content or dependencies** (*I personally recommend `ModulesHost` as for the name*) and add a reference to **BlazorLazyLoading.Module**:
```xml
<PackageReference Include="BlazorLazyLoading.Module" Version="1.1.0" PrivateAssets="all" />
```

1. Reference all the RazorLib projects that you want to lazy load by using `ProjectReference` or `PackageReference`. For example:
```xml
<ItemGroup>
<ProjectReference Include="../MyComponents.csproj" />
<PackageReference Include="isc30.MyPages" />
</ItemGroup>
```

2. Include the referenced Assemblies in the Manifest generation:<br/>
```xml
<ItemGroup>
<BLLManifestAssemblies Include="MyComponents" />

<!-- notice the assembly name for the nuget package -->
<BLLManifestAssemblies Include="MyPages" />
</ItemGroup>
```

# Advanced scenario: Skipping the Manifest Generation

If you wish to load the module manually by using `IAssemblyLoader`, you can disable the manifest generation step by setting `BLLGenerateManifest` to `false`:

```xml
<PropertyGroup>
<BLLGenerateManifest>false<BLLGenerateManifest>
</PropertyGroup>
```
1 change: 1 addition & 0 deletions docs/Going-Raw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
To be written. This section will be about `IAssemblyDataLocator`, `IAssemblyLoader`, etc
19 changes: 19 additions & 0 deletions docs/Home.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Welcome to *Blazor Lazy Loading* wiki!

Please use the **Wiki Index** panel to navigate to the different pages --->

***

# FAQ

- Is WebAssembly supported?

>Yes, every host of Blazor is supported. This includes Blazor Server, WebAssembly DevServer, WebAssembly static (published), Prerendering and even DualMode.
- For WASM, can I keep the linker enabled?

>Yes, even if disabling it doesn't make a big difference when your application uses lazy loading, you can provide a custom LinkerConfig.xml file to enable it. [Mode info...](https://github.com/isc30/blazor-lazy-loading/wiki/Configuring-Lazy-Loading-@-WebAssembly#configuring-the-linker)
- Can my Lazy Modules contain references to NuGet packages?

>Yes, every nested dependency of a Module (including NuGet DLLs) will have a Lazy download that will be triggered only when required.
1 change: 1 addition & 0 deletions docs/_Footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*[https://github.com/isc30/blazor-lazy-loading](https://github.com/isc30/blazor-lazy-loading)*
12 changes: 12 additions & 0 deletions docs/_Sidebar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Wiki Index

* [Home / FAQ](https://github.com/isc30/blazor-lazy-loading/wiki)
* [Creating a Lazy Module](Creating-a-Lazy-Loadable-Module)
* [Migrating an existing Application](Creating-a-Lazy-Loadable-Module#creating-an-aggregated-module)
* [Configuring Lazy Loading](Configuring-Lazy-Loading)
* [WebAssembly](Configuring-Lazy-Loading-@-WebAssembly)
* [Server](Configuring-Lazy-Loading-@-Server)
* [Lazy Components](Components)
* [&lt;Lazy&gt;](Components-@-Lazy)
* [&lt;LazyRouter&gt;](Components-@-LazyRouter)
* [Going Raw](Going-Raw)
File renamed without changes
File renamed without changes
File renamed without changes