LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

WEB前段开发如何使用流式渲染技术提升用户体验

admin
2024年12月11日 11:24 本文热度 186

什么是流式渲染?

流式渲染主要思想是将HTML文档分块(chunk)并逐块发送到客户端,而不是等待整个页面完全生成后再发送。

流式渲染不是什么新鲜的技术。早在90年代,网页浏览器就已经开始使用这种方式来处理HTML文档。

在 SPA (单页应用)流行的时代,由于 SPA 的核心是客户端动态地渲染内容,流式渲染没有得到太多关注。如今,随着服务端渲染相关技术的成熟,流式渲染成为可以显著提升首屏加载性能的利器。

素材来源于文章

Node.js 实现简单流式渲染

HTTP is a first-class citizen in Node.js, designed with streaming and low latency in mind. This makes Node.js well suited for the foundation of a web library or framework.

HTTP 是 Node.js 中的一等公民,其设计时考虑到了流式传输和低延迟。这使得 Node.js 非常适合作为 Web 库或框架的基础。

———— Node.js官网

Node.js 在设计之初就考虑到了流式传输数据,考虑如下代码:

const Koa = require('koa');
const app = new Koa();

// 假设数据需要 5 秒的时间来获取
renderAsyncString = async () => {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('<h1>Hello World</h1>');
   }, 5000);
 })
}

app.use(async (ctx, next) => {
 ctx.type = 'html';
 ctx.body = await renderAsyncString();
 await next();
});

app.listen(3000, () => {
 console.log('App is listening on port 3000');
});

这是一个简化的业务场景,运行起来后,会在5秒的白屏后显示一段 hello world 文字。

没有用户会喜欢一个会白屏5秒的网页!在 web.dev 对 TTFB 的介绍中,加载第一个字节的时间应该在 800ms 内才是良好的 web 网站服务。

我们可以利用流式渲染技术来改善这一点,先通过渲染一个 loading 或者骨架屏之类的东西来改善用户体验。查看改进后的代码:

const Koa = require('koa');
const app = new Koa();
const Stream = require('stream');

// 假设数据需要 5 秒的时间来获取
renderAsyncString = async () => {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('<h1>Hello World</h1>');
   }, 5000);
 })
}

app.use(async (ctx, next) => {
 const rs = new Stream.Readable();
 rs._read = () => {};
 ctx.type = 'html';
 rs.push('<h1>loading...</h1>');
 ctx.body = rs;
 renderAsyncString().then((string) => {
   rs.push(`<script>
     document.querySelector('h1').innerHTML = '${string}';
   </script>`);
 })
});

app.listen(3000, () => {
 console.log('App is listening on port 3000');
});

使用流式渲染后,这个页面最初显示 "loading...",然后在 5 秒后更新为 "Hello World"。

需要注意的是:Safari 浏览器对于何时触发流式传输可能有一些限制(以下内容未找到官方说明,通过实践总结得到):

  • 传输的 chunk 需大于 512 字节

  • 传输的内容需能够在屏幕上实际渲染,例如传输 <div style="display:none;">...</div> 可能是不生效的。

声明式 Shadow DOM,不依赖 javascript 实现

上面的代码中,我们用到了一些 javascript,本质上我们需要预先渲染一部分 html 标签作为占位,之后在用新的 html 标签去替换他们。这用 javascript 很好实现,如果我们禁用了 javascript 呢?

这可能需要一些 Shadow DOM 的技巧!很多组件化设计前端框架都有 slot 的概念,在 Shadow DOM 中也提供了 slot 标签,可以用于创建可插入的 Web Components。在 chrome 111 版本以上,我们可以使用声明式 Shadow DOM,不依赖 javascript,在服务器端使用 shadow DOM。一个声明式 Shadow DOM 的例子:

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

   <div slot="hole">插入一段文字!</div>

渲染结果如下:

可以看到,我们的文字被插入在了 slot 标签中,利用声明式 Shadow DOM,我们可以改写上面的例子:

const Koa = require('koa');
const app = new Koa();
const Stream = require('stream');

// 假设数据需要 5 秒的时间来获取
renderAsyncString = async () => {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('<h1>Hello World</h1>');
   }, 5000);
 })
}

app.use(async (ctx, next) => {
 const rs = new Stream.Readable();
 rs._read = () => {};
 ctx.type = 'html';
 rs.push(`
 <template shadowrootmode="open">
   <slot name="hole"><h1>loading</h1></slot>
 </template>
 `);
 ctx.body = rs;
 renderAsyncString().then((string) => {
   rs.push(`<h1 slot="hole">${string}</h1>`);
   rs.push(null);
 })
});

app.listen(3000, () => {
 console.log('App is listening on port 3000');
});

运行这段代码,和之前的代码结果完全一致,不同的,当我们禁用掉浏览器的 javascript,代码也一样正常运行!

声明式 Shadow DOM 是一个比较新的特性,可以在这篇文档中看到更多内容。

react 实现流式渲染

我们换个视角看看 react,react 18 之后在框架层面上支持了流式渲染, 下面是使用 nextjs 改写上面的代码:

import { Suspense } from 'react'


const renderAsyncString = async () => {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve('Hello World!');
   }, 5000);
 })
}

async function Main() {
 const string = await renderAsyncString();
 return <h1>{string}</h1>
}

export default async function App() {
 return (
   <Suspense fallback={<h1>loading...</h1>} >
     <Main />
   </Suspense>
 )
}

运行这段代码,和之前的代码结果完全一致,同样也不需要运行任何客户端的 javascript 代码。

关于 react 的流式渲染在这里能看到官方技术层面上的解释。本文作为对于流式渲染的概览,不作更细致的讲解。

总结

本文从理论上探讨了流式渲染相关实现方案,理论上,流式渲染很简单。HTTP 标准和 Node.js 很早之前就支持了这一特性。但在工程实践中,它很复杂。例如对于 react 来说,流式渲染不仅仅需要 react 作为 UI 来支持,也需要借助 nextjs 这种元框架(meta framework)提供服务端的能力。

原文链接:https://juejin.cn/post/7347009547741495350

作者: 李章鱼


该文章在 2024/12/11 11:24:41 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved