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

[删除(380066935@qq.com或微信通知)]

更好的阅读体验请查看原文:https://blog.csdn.net/qq_32174441/article/details/122350559

网上已经有很多资料,但大多都是只支持一个模式切换,这次实现了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模式了。

————————————————

版权声明:本文为CSDN博主「北音执念~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_32174441/article/details/122350559