ltrzesniewski / RazorBlade

Compile Razor templates at build-time without a dependency on ASP.NET.
MIT License
149 stars 7 forks source link

Streaming HTML #13

Closed naasking closed 6 months ago

naasking commented 7 months ago

There was an interesting HN post recently on streaming HTML without JavaScript, and it got me thinking about how to flush partially rendered HTML in RazorBlade.

Basically, the idea is that the HTML rendered up to the "FLUSH" marker is sent immediately because the content after might take awhile to render:

<div>
  <template shadowrootmode="open">
    <header>Header</header>
    <main>
      <slot name="content"></slot>
    </main>
    <footer>Footer</footer>
  </template>

  <!-- FLUSH -->
  <div slot="content">
    This div will be rendered inside the slot above. Magic!
  </div>
</div>

If RazorBlade used the output stream directly I could invoke it with a TextWriter whose stream was directly attached to a network socket and then directly call Flush/FlushAsync:

<div>
  <template shadowrootmode="open">
    <header>Header</header>
    <main>
      <slot name="content"></slot>
    </main>
    <footer>Footer</footer>
  </template>

  @(await Output.FlushAsync())

  <div slot="content">
    This div will be rendered inside the slot above. Magic!
  </div>
</div>

But it looks like you first render to a string builder, then write that to the output. I think this is a bit outside of your original use case, but is there a compelling reason to not render directly to the given TextWriter?

ltrzesniewski commented 7 months ago

Well, the initial implementation wrote directly to the output writer without intermediate buffering, but I had to change it in v0.5.0 in order to support layouts (where you need to call @RenderBody() in the layout page).

I'm not very happy about that either, but I thought this was good enough for a library like this one - I didn't think anyone would want to stream the output with RazorBlade.

I'll think about how to improve it. In the meantime, maybe you could use v0.4.4 as a workaround if you don't need support for layouts.

ltrzesniewski commented 6 months ago

I released v0.6.0 which adds a FlushAsync method that works similarly to the one in ASP.NET.

Note that you'll have to write @await FlushAsync() in your template (without the Output), and that this feature is incompatible with layouts (which really do require buffering).

I may add an option to disable buffering and write directly to the output stream, but I'll leave that for a future version.