Blzaor的Wasm和Server模式无缝切换,混合使用

网上已经有很多资料,但大多都是只支持一个模式切换,这次实现了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<Caviar.AntDesignUI.App>("#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}";

}
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>Caviar</title>
    <base href="/" />
</head>

<body>

    @if(IsServer)
    {
        <app>
            <component type="typeof(Caviar.AntDesignUI.App)" render-mode="ServerPrerendered" />
            <persist-component-state />
        </app>
        <script src="_framework/blazor.server.js"></script>
    }
    else
    {
        <div id="iframe_div" style="width:100%; height:100%;border:medium none">
            <iframe id="iframe_Server" src='@serverHost' style="width:100%; height:100%;border:medium none"></iframe>
        </div>
        <app id="wasm_app" style="display: none;">
            <component type="typeof(Caviar.AntDesignUI.App)" render-mode="WebAssemblyPrerendered">
        </app>
        <script src="_framework/blazor.webassembly.js"></script>
    }

    <link href="_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet" id="uiCss" media="screen">
    <script src="_content/AntDesign/js/ant-design-blazor.js"></script>
    <script src="_content/Caviar.AntDesignUI/js/Caviar.js"></script>
    <script src="_content/Caviar.AntDesignUI/js/prism.js"></script>
    <link href="_content/Caviar.AntDesignUI/js/prism.css" rel="stylesheet" />
</body>

</html>

当然这里做了一些处理,如果同时加载两个模型,因为会同事订阅事件,会互相处理,所以会卡死,我们直接用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 id="iframe_Server" src="' + url + '?IsAutomaticSwitchWasm=false&server=true" style="width:100%; height:100%;border:medium none"></iframe>'
	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
{
    /// <summary>
    /// iframe与框架通信类
    /// </summary>
    public class IframeMessage
    {
        /// <summary>
        /// 调用的模式
        /// </summary>
        public Pattern Pattern { get; set; }
        /// <summary>
        /// 转到的地址
        /// </summary>
        public string Url { get; set; }
        /// <summary>
        /// 传输的数据
        /// </summary>
        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
<CascadingAuthenticationState>
    <Router @ref="UserConfig.Router" AppAssembly="@typeof(Caviar.AntDesignUI.Config).Assembly"
            AdditionalAssemblies="Config.AdditionalAssemblies"
            PreferExactMatches="@true">
        <Found Context="routeData">
            <AuthorizeView>
                <Authorized>
                    <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(CavMainLayout)" />
                    @*<ReuseTabsRouteView RouteData="@routeData" DefaultLayout="@typeof(CavMainLayout)" />*@
                </Authorized>
                <NotAuthorized>
                    <Caviar.AntDesignUI.Pages.User.Login></Caviar.AntDesignUI.Pages.User.Login>
                </NotAuthorized>
            </AuthorizeView>
        </Found>
        <NotFound>
            <Caviar.AntDesignUI.Pages.Exception._404._404></Caviar.AntDesignUI.Pages.Exception._404._404>
        </NotFound>
    </Router>
    <AntContainer />
</CascadingAuthenticationState>
@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模式了。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值