网上已经有很多资料,但大多都是只支持一个模式切换,这次实现了Wasm和Server无缝切换,并且去掉了Wasm首次加载时间过长的问题,利用Server先进行访问,等后台资源下载完成后,切换到Wasm模式,减少占用服务器资源和长连接的问题
先看视频,如果不是想要的效果就不用往下看了
Blazor的Wasm和Server模式无缝切换https://www.bilibili.com/video/BV1L3411e7o9/
首先,没有单独写例子,集成到了我的开源软件里,可以查看 Caviar-Blazor,内容有点多,慢慢来分解一下。
有些限制,需要编写的时候按照Wasm进行编码,也就是所有的数据请求需要用Http,不能直接使用对象,然后建立两个项目,一个是server模式,一个是wasm模式,server模式的引用wasm模式就可以,需要把wasm模式里一段代码屏蔽掉,按照自己的来
builder.RootComponents.Add("#app");
然后就是在server的_Host.cshtml加载两个模型
@page "_Host"
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using Caviar.AntDesignUI
@using Caviar.Infrastructure
@using Caviar.SharedKernel
@using Microsoft.Extensions.Options
@{
var IsServer = Request.Query.ContainsKey(CurrencyConstant.ServerName);
var host = Request.Host;
var serverHost = $"{Request.Scheme}://{host}?{CurrencyConstant.ServerName}";
}
Caviar
@if(IsServer)
{
}
else
{
}
当然这里做了一些处理,如果同时加载两个模型,因为会同事订阅事件,会互相处理,所以会卡死,我们直接用iframe进行隔离,我们先显示server模式,把wasm模式隐藏起来,这样就像直接使用srever模式一样。自动切换我们等会再说 ,我们先实现手动切换
现在思路就很清除了,我们可以隐藏iframe也可以直接去掉来节省资源,我们只需要两个js就能搞定
function switch_wasm() {
wasm_app = document.getElementById("wasm_app");
iframe_div = document.getElementById("iframe_div");
wasm_app.style.display = "block";
iframe_div.style.display = "none";
var iframe = document.getElementById("iframe_Server");
iframe.parentNode.removeChild(iframe);
}
function switch_server(url) {
wasm_app = document.getElementById("wasm_app");
wasm_app.style.display = "none";
iframe_div = document.getElementById("iframe_div");
iframe_div.innerHTML = ''
iframe_div.style.display = "block"
}
切换wasm模式时,直接移除iframe,然后将wasm显示
切换wasm模式时,重新加载iframe,然后隐藏wasm,非常完美
后面我们需要在wasm加载完毕时,自动切换,那么切换数据的时机很重要,不能在用户操作时候切换,需要在用户切换页面的时候,直接将wasm放出来,用户就感觉不到了。所以我们需要一个事件,当用户切换页面时触发事件,如果加载好了就切换没有加载好就不切换。
这就涉及到了iframe和外面进行通信,好在已经有解决方案
//iframe内发送消息
function iframeMessage(message) {
window.parent.postMessage(message, '*');
}
//iframe外监听
window.addEventListener('message', function (e) {
console.log(e.data);
DotNet.invokeMethod("Caviar.AntDesignUI", "JsNavigation", e.data)
})
iframe只需要调用postMessage,外面就可以监听到,这样通信也有了
//在server模式下且需要自动切换
if (Config.IsServer && UserConfig.IsAutomaticSwitchWasm)
{
var iframeMessage = new IframeMessage();
iframeMessage.Pattern = Pattern.Wasm;
iframeMessage.Url = menuItem.RouterLink;
_ = jSRuntime.InvokeVoidAsync("iframeMessage", iframeMessage);
}
在你需要切换的位置进行调用,当然我们需要判断一下当前是否为server模式,不然在wasm也会调用,还需要判断一下是否需要自动切换,当我们调用js以后,js会调用c#的一个静态方法
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Caviar.AntDesignUI
{
///
/// iframe与框架通信类
///
public class IframeMessage
{
///
/// 调用的模式
///
public Pattern Pattern { get; set; }
///
/// 转到的地址
///
public string Url { get; set; }
///
/// 传输的数据
///
public object Data { get; set; }
public delegate void JSScheduling(IframeMessage message);
public static event JSScheduling SwitchWasm;
[JSInvokable]
public static void JsNavigation(IframeMessage message)
{
switch (message.Pattern)
{
case Pattern.Wasm:
SwitchWasm?.Invoke(message);
break;
default:
break;
}
}
}
public enum Pattern
{
Wasm,
Server
}
}
在方法里调用事件即可,剩下的就是合适注册事件,所有的组件都是从app开始的,所以app里写再合适不过了
@using Caviar.AntDesignUI.Helper; @using System.Web@code { [Inject] UserConfig UserConfig { get; set; } [Inject] NavigationManager NavigationManager { get; set; } [Inject] IJSRuntime JSRuntime{ get; set; } protected override void OnParametersSet() { if (!Config.IsServer) { //wasm模式初始化完成,接收事件 IframeMessage.SwitchWasm += SwitchWasm_RefChanged; } else { var uri = new Uri(NavigationManager.Uri); var collection = HttpUtility.ParseQueryString(uri.Query); if (!string.IsNullOrEmpty(collection["IsAutomaticSwitchWasm"])) { UserConfig.IsAutomaticSwitchWasm = bool.Parse(collection["IsAutomaticSwitchWasm"]); } } base.OnParametersSet(); } private void SwitchWasm_RefChanged(IframeMessage message) { NavigationManager.NavigateTo(message.Url); JSRuntime.InvokeVoidAsync("switch_wasm"); } } @* *@
在OnParametersSet时判断一下是否为wasm模式,如果是就注册事件既可,事件里进行wasm跳转,并且切换到wasm模式。
当然,在url里需要表明当前是否需要自动切换,不然就不能使用server模式了。



