我们能否在一个普通的非Blazor HTML页面中将Blazor组件作为Web组件使用?

7

我们能否将 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的一部分。到目前为止,我遇到了以下问题:显然,这不是2018年Blazor设计目标之一:https://github.com/dotnet/aspnetcore/issues/16033。后来,Steve Sanderson创建了一个(非官方)库来测试隔离的Blazor组件,理论上可以用来获得独立的Blazor组件标记:https://dev59.com/w1IH5IYBdhLWcg3wOrO7#60457390。到目前为止,这是解决这个问题的最佳方法吗?

1
我不太喜欢在同一个前端项目中_混合_使用技术。过去有太多坏经验了。许多事情都从Blazor中_控制_,特别是在服务器端使用Signalr更新UI的情况下。我不记得你提到的库,但我认为它应该作为预渲染HTML集成工作,将组件的管理_放置在_Blazor电路之外。请参阅https://chrissainty.com/using-blazor-components-in-an-existing-mvc-application/,以获得现有MVC项目集成的示例。 - Nicola Biada
2
是的,我在身份验证服务器部分使用相同的方法来混合 Blazor 组件和 MVC 视图页面,这仍然是 cshtml 格式。 - Nicola Biada
1
@Webreaper 在 Twitter 上提到,Florian Rappl 的项目 Piral 能够做到这一点。据我理解,链接为 https://github.com/smapiot/Piral.Blazor。 - noseratio - open to work
2
已添加带有一些代码的答案。相同的代码已添加到https://github.com/dotnet/aspnetcore/issues/35551中。 - Nicola Biada
此外:https://visualstudiomagazine.com/articles/2021/09/16/aspnet-core-updates.aspx - noseratio - open to work
显示剩余4条评论
3个回答

8

2
相关链接:https://github.com/dotnet/aspnetcore/issues/35372 - noseratio - open to work
有没有这个的更新链接或片段?两条路径都导致了一个404页面。 - Ddddan

3

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

例如,下面这个文件是由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 组件。


这很棒。当你说“静态渲染”时,是指组件不具有交互性吗(即,只呈现具有样式的静态组件,没有动态行为)?还是你指的是其他什么 (我的MVC有点生疏,所以可能还有其他意思我不知道)。 - Webreaper
2
静态渲染意味着HTML将按原样呈现,没有js或wasm在第二阶段中进行变异。 - Nicola Biada

2
不确定这是否有帮助,但您绝对可以从服务器端页面执行它(如果不行我将删除此答案)。这是一个测试页面,在cshtml页面中呈现了所有三个标准“页面”和服务器端内容。在Blazor中,您需要实际忘记“页面”概念。 EVERYTHING都是组件。页面只是带有Page自定义属性的组件。
这种设置的问题在于,一旦刷新页面,您就会重新启动三个组件,并且会丢失任何作用域数据。
@page "/test"
@namespace StackOverflow.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>StackOverflow.Answers</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="StackOverflow.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(StackOverflow.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(StackOverflow.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(StackOverflow.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(StackOverflow.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(StackOverflow.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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接