我的编程空间,编程开发者的网络收藏夹
学习永远不晚

我们一起聊聊如何通过流式渲染提升用户体验?

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

我们一起聊聊如何通过流式渲染提升用户体验?

流式渲染并非新兴技术,早在 90 年代,网页浏览器就已开始运用这种模式来处理 HTML 文档。不过,在 SPA(单页应用)大行其道的时期,由于其核心在于客户端动态渲染内容,流式渲染未能引起广泛关注。然而,现今随着服务端渲染技术的日臻成熟,流式渲染已成为显著优化首屏加载性能的有力手段。

Node.js 实现简单流式渲染

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('');
    }, 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[1] 对于 TTFB(Time To First Byte,首字节时间)的介绍中提到,加载第一个字节的时间应当控制在 800ms 以内,才能称得上是优质的 Web 网站服务。

为了改善这种情况,我们可以借助流式渲染技术。比如,先向用户呈现一个加载中的提示或者骨架屏,以此来优化用户体验。下面是改进后的代码:

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

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

app.use(async (ctx, next) => {
  const rs = new Stream.Readable();
  rs._read = () => {};
  ctx.type = 'html';
  rs.push('');
  ctx.body = rs;
  renderAsyncString().then((string) => {
    rs.push(``);
  })
});

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

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

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

  • 传输的 chunk 大小需大于 512 字节。若小于此值,可能无法有效触发流式传输,影响用户体验。
  • 传输的内容必须能够在屏幕上实际渲染。例如,传输
    ...
    这样隐藏的内容可能是无效的,无法实现流式渲染的预期效果。

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

在上述的代码中,我们运用了一定的 JavaScript 代码。本质上,我们需要预先渲染一部分 HTML 标签作为占位,随后再用新的 HTML 标签对其进行替换。使用 JavaScript 来实现这一过程相对容易,但如果禁用了 JavaScript 呢?

这就可能需要借助一些 Shadow DOM[2] 的技巧!众多组件化设计的前端框架都包含了 slot(插槽)的概念,在 Shadow DOM 中也提供了 slot 标签,其可用于创建可插入的 Web Components。在 Chrome 111 及以上版本中,我们能够使用声明式 Shadow DOM,无需依赖 JavaScript,在服务器端就能实现 shadow DOM 的功能。以下是一个声明式 Shadow DOM 的示例:


  
    
插入一段文字!

从中可以清晰地看到,我们的文字成功插入到了 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('');
    }, 5000);
  })
}

app.use(async (ctx, next) => {
  const rs = new Stream.Readable();
  rs._read = () => {};
  ctx.type = 'html';
  rs.push(`
  
  `);
  ctx.body = rs;
  renderAsyncString().then((string) => {
    rs.push(``);
    rs.push(null);
  })
});

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

运行这段改写后的代码,其结果与之前完全相同。更为重要的是,即便我们禁用了浏览器的 JavaScript,代码依然能够正常运行!

声明式 Shadow DOM 是一个相对较新的特性,您可以在这篇文档[3]中获取更多详细信息。

react 实现流式渲染

现在让我们转换视角,来看看 React 框架中的流式渲染。自 React 18 版本之后,在框架层面上开始支持流式渲染。下面是使用 nextjs 对之前的示例进行改写的代码:

import { Suspense } from 'eact'

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

async function Main() {
  const string = await renderAsyncString();
  return 
}

export default async function App() {
  return (
    
      
) }

运行这段代码,其效果与之前的示例完全一致,并且同样无需运行任何客户端的 JavaScript 代码。

关于 React 的流式渲染,您可以在官方的技术层面[4]解释中获取更深入的信息。在本文中,仅作为对流式渲染的概要介绍,不对其进行更为细致的讲解。

总结

本文从理论层面深入探讨了流式渲染的相关实现方案。理论上,流式渲染的概念和实现相对简单。HTTP 标准和 Node.js 早在很久以前就对这一特性提供了支持。然而,在实际的工程应用中,流式渲染并非易事。以 React 为例,要实现流式渲染,不仅需要 React 自身作为用户界面(UI)框架提供支持,还需要借助像 nextjs 这样的元框架(meta framework)来赋予服务端相应的能力。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

我们一起聊聊如何通过流式渲染提升用户体验?

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

我们一起聊聊如何通过流式渲染提升用户体验?

本文从理论层面深入探讨了流式渲染的相关实现方案。理论上,流式渲染的概念和实现相对简单。HTTP 标准和 Node.js 早在很久以前就对这一特性提供了支持。

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录