我们可以在常规非 Blazor HTML 页面中将 Blazor 组件作为 Web 组件使用吗?

Can we consume a Blazor component as a Web Component within a regular non-Blazor HTML page?

我们能否将 Blazor 组件呈现为独立的 DOM 片段,或者以其他方式将其作为普通 HTML/JS 页面中的标准 Web 组件使用?

从 Blazor 体系结构的角度来看,这可能是一个幼稚的问题。到目前为止,我不是 Blazor 专家,但我认为它可以成为遗留 Web 应用程序增量“棕地”现代化的有用技术。我很惊讶这似乎没有得到官方支持。

为了说明,考虑这个简单的 Web 组件示例,它呈现一个自定义元素 <date-info>:

// define a custom web component
customElements.define("date-info", class DateInfo extends HTMLElement {
  constructor() {
    super();
    // create an "open" (vs "closed") shadow DOM, 
    // i.e., accessible to the outside JavaScript
    this.attachShadow({ mode: "open" });
  }

  async connectedCallback() {
    console.log(`${this.constructor.name}.${this.connectedCallback.name} called`);

    // get the content from the server, 
    // this could be a Blazor component markup
    try {
      const response = await fetch("https://worldtimeapi.org/api/ip");
      const data = await response.json();
      const content = new Date(data.utc_datetime).toString();
      this.shadowRoot.innerHTML = `<span>${content}</span>`;
    }
    catch(e) {
      console.error(e);
      const info = document.createTextNode(e.message); 
      this.shadowRoot.appendChild(info);
    }
  }
});
<!-- use the web component --> 
<p>Current time: <date-info/></p>

现在,我不想获取 https://worldtimeapi.org/api/ip,而是想获取并呈现 Blazor/Server 组件的分离标记,例如:

@* this is a Blazor component *@
<p>@(DateTime.Now)</p>

此外,我希望此标记保持功能性和动态性,即客户端 DOM 事件和此 Blazor 组件的服务器端更新在整个生命周期中进一步双向传播包装 Web 组件的周期。

当然可以将其设为 Blazor @page 并将其加载到 iframe 中,但我更希望将其呈现为外部页面 DOM 的一部分.

到目前为止,我遇到过这个:

MS 已解决此限制,但解决方案需要 .Net 6。

https://github.com/aspnet/AspLabs/tree/main/src/BlazorCustomElements

这是史蒂夫·桑德森本人完成的。

不确定这是否有帮助,但您绝对可以从服务器端页面执行此操作(如果没有帮助,我将删除此答案)。这是一个测试页面,它在具有服务器端内容的 cshtml 页面中呈现所有三个标准“页面”。您实际上需要忘记 Blazor 中的“页面”概念。一切都是组件。页面只是具有 Page 自定义属性的组件。

此设置的问题是,一旦您刷新页面,您就会重新启动这三个组件,并且您会丢失任何范围内的数据。

@page "/test"
@namespace Whosebug.Answers.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Whosebug.Answers</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="Whosebug.Answers.styles.css" rel="stylesheet" />
</head>
<body>
    <div class="m-2 p-s bg-light">
        <h3>A Normal Razor Page</h3>
        <p>
            Lots of server side rendered junk
        </p>
        <component type="typeof(Whosebug.Answers.Shared.SurveyPrompt)" render-mode="ServerPrerendered" />
    </div>
    <div class="m-2 p-s bg-info">
        <h3>A Normal Header</h3>
        <p>
            Lots More server side rendered junk
        </p>
        <component type="typeof(Whosebug.Answers.Pages.Counter)" render-mode="ServerPrerendered" />
    </div>
    <div class="m-2 p-s bg-light">
        <h3>A Normal Header</h3>
        <p>
            Lots More server side rendered junk
        </p>
        <component type="typeof(Whosebug.Answers.Pages.Counter)" render-mode="ServerPrerendered" />
    </div>

    <div class="m-2 p-s bg-light">
        <h3>Yet Another Normal Header</h3>
        <p>
            Lots More server side rendered junk
        </p>
        <component type="typeof(Whosebug.Answers.Pages.FetchData)" render-mode="ServerPrerendered" />
    </div>

    <div class="m-2 p-s bg-light">
        <h3>Yet Another Normal Header</h3>
        <p>
            Lots More server side rendered junk
        </p>
        <component type="typeof(Whosebug.Answers.Pages.FetchData)" render-mode="ServerPrerendered" />
    </div>


    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss"></a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

与此同时,您可以将旧的 cshtml 与剃刀组件混合
我使用这种方法在两个系统之间保持相同的图形布局

举个例子,下面的文件是Identity.
使用的_Layout.cshtml 我通过静态渲染使用了各种 Blazor 组件:

@using Microsoft.AspNetCore.Hosting
@using Microsoft.AspNetCore.Mvc.ViewEngines
@inject IWebHostEnvironment Environment
@inject ICompositeViewEngine Engine
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using Project.Server.Shared

<!DOCTYPE html>
<html>
<head>
    <component type="typeof(MainLayoutHead)" render-mode="Static" />
</head>
<body>
    <app>
        <div class="container main">
            <component type="typeof(MainLayoutTopImages)" render-mode="Static" />
            <div class="row navmenu-row">
                <div class="col-md-12 bg-dark navmenu-col">
                    <component type="typeof(NavMenu)" render-mode="Static" />
                </div>
            </div>
            <div class="row content pt-4 pb-0 mt-0">
                <div class="col-md-12">
                    <div class="row">
                        <div class="col-md-12">
                            @*Required for GDPR.*@
                            <partial name="_CookieConsentPartial" />
                        </div>
                    </div>
                    <div class="row body-row">
                        <div class="col-md-12 body-col">
                            @RenderBody()
                        </div>
                    </div>


                </div>
            </div>
            <component type="typeof(MainLayoutFooter)" render-mode="Static" />
        </div>
    </app>

    <script src="~/Identity/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/Identity/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/Identity/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>

MainLayoutHeadMainLayoutFooterNavMenu 是常规的 Blazor 组件。