前端使用ReadableStream.getReader来处理流式渲染

news2025/1/17 0:45:37

文章目录

  • 前言
  • 一、纯css
  • 二、vue-typed-js插件
    • 1.安装
    • 2.注册
    • 3.使用
    • 总结
  • 三、ReadableStream
    • 1.ReadableStream是什么?
    • 2.ReadableStream做什么?
    • 3.ReadableStream怎么用


前言

需求:让接口返回的文章根据请求一段一段的渲染,同时可以点击“停止生成”按钮后停止请求。
经历:使用纯css,vue-typed-js插件,ReadableStream 三种,虽然前两种并非很完美,但这种一步步排错并解决问题的思路在我看来更为重要。如果你急需判断是否有效果,也可以直接看第三步。
接口返回的数据:由于是一段一段渲染,所以是数组套对象,如下,完整的句子是“我曾满怀激情地憧憬过,也曾经历了无数次失望和沮丧,但最终还是来到了这里。”

[
    {
        "header":{
            "code":"200"
        },
        "chunk":"我"
    },
    {
        "header":{
            "code":"200"
        },
        "chunk":"曾满怀激情地"
    },
    {
        "header":{
            "code":"200"
        },
        "chunk":"憧憬过,也"
    },
    {
        "header":{
            "code":"200"
        },
        "chunk":"曾经历了无数次失望"
    },
    {
        "header":{
            "code":"200"
        },
        "chunk":"和沮丧,但最终还是"
    },
    {
        "header":{
            "code":"200"
        },
        "chunk":"来到了这里。"
    }
]

一、纯css

由于内容较多,这里只会说一下思路,具体的内容会提供原文链接进行查看。
将所有的内容用p标签包裹,默认是隐藏的。然后加上动画,第一个0.5s显示,第二个1s显示,第三个1.5s显示…以此类推。
缺点:需要手动给n个p标签加上样式,而且内容要是固定的才可以,若返回的是不同数据就无法使用了。当然了,博客的title也写的很清除了,让文字一行一行出现或者显示。

参考文章:
css动画让文字一行一行出现: https://www.5axxw.com/questions/simple/dkog6u
css动画让文字一行一行逐渐显示: https://www.5axxw.com/questions/simple/aawr87
https://www.5axxw.com/questions/simple/vy1p34: https://www.5axxw.com/questions/simple/vy1p34

二、vue-typed-js插件

官网地址: https://github.com/Orlandster/vue-typed-js

1.安装

npm install --save vue-typed-js

2.注册

import Vue from 'vue'
import VueTypedJs from 'vue-typed-js'
 
Vue.use(VueTypedJs)

3.使用

<vue-typed-js
   :startDelay="1000"
   :cursorChar="'|'"
   :strings="[
     '<span>我</span>',
     '<span>曾满怀激情地</span>',
     '<span>憧憬过,也曾</span>',
     '<span>经历了无数次失望和沮丧,</span>',
     '<span>但最终还是</span>',
     '<span>来到了这里。</span>',
   ]"
   :contentType="'html'"
 >
 </vue-typed-js>

字段详解:
startDelay 输入延迟时间,单位ms
cursorChar设置光标长什么样子
strings 一个数组,里面是要渲染的数据
contentType 输入的是文本还是html

总结

没有什么坑的,直接安装注册使用即可。但这有一个严重问题我解决不了。strings这个渲染的数组,如果你在vue中watch监听这个数据,每次改变的话,你会发现有旧值和新值,但如果是接口返回的数据,就只会返回数组的第一个对象的值,后面的不会返回拼接数据了,也就是strings在vue-typed-js组件中只会触发一次,

三、ReadableStream

ReadableStream MDN 官网介绍: https://developer.mozilla.org/zh-CN/docs/Web/API/ReadableStream

1.ReadableStream是什么?

Stream API 中的 ReadableStream 接口表示可读的字节数据流。Fetch API 通过 Response 的属性 body (en-US) 提供了一个具体的 ReadableStream 对象。

2.ReadableStream做什么?

我的理解:就是把接口的内容返回 base64 的字节,然后通过getReader()创建一个 reader,并将流锁定。只有当前 reader 将流释放后,其他 reader 才能使用。通过 cancel() 方法返回一个 Promise,这个 promise 会在流被取消的时候兑现。cancel 用于在不再需要来自它的任何数据的情况下(即使仍有排队等待的数据块)完全结束一个流。调用 cancel 后该数据丢失,并且流不再可读。

3.ReadableStream怎么用

(1)你可以将以下代码通过设置了跨域的谷歌浏览器的打开

若你没有设置了跨域的谷歌浏览器,请查看我写的博客【谷歌(Chrome)浏览器的设置跨域】来创建一个跨域的浏览器:https://blog.csdn.net/weixin_44784401/article/details/131111077

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script>
  fetch("https://www.example.org")
  .then((response) => response.body)
  .then((rb) => {
    const reader = rb.getReader();
    return new ReadableStream({
      start(controller) {
        function push() {
          reader.read().then(({ done, value }) => {
            if (done) {
              console.log("done111", done);
              controller.close();
              return;
            }
            controller.enqueue(value);
            console.log("done222", done, value);
            push();
          });
        }
        push();
      },
    });
  })
  
  </script>
</body>
</html>

(2)效果
在这里插入图片描述
由上图可验证: reader.read().then(({ done, value })中的value表示ReadableStream可读的字节数据流。
(3)结合实际情况进行流式渲染数据的伪代码,我的是vue2 项目

// 注意:以下代码是伪代码,需要你结合自身需求去修改
let _this = this; // 存储this
  let data = '';
  fetch("https://www.example.org", {
    method: 'POST',
    body: JSON.stringify(data)
  })
  .then((response) => {
      const reader = response.body.getReader(); // 创建一个读取器并将流锁定于其上。一旦流被锁定,其他读取器将不能读取它,直到它被释放
      const decoder = new TextDecoder(); // TextDecoder 接口表示一个文本解码器,一个解码器只支持一种特定文本编码,例如 UTF-8、ISO-8859-2、KOI8-R、GBK,等等。解码器将字节流作为输入,并提供码位流作为输出。
      console.log('000', response.body, decoder);
      // 按顺序读取每个分块,并传递给 UI,当整个流被读取完毕后,从递归方法中退出,并在 UI 的另一部分输出整个流。
      function read() {
        // read() 返回了一个 promise 
        // done  - 当 stream 传完所有数据时则变成 true
        // value - 数据片段。当 done 为 true 时始终为 undefined
        return reader.read().then(({ done, value }) => {
          if (done) {
            return;
          }
          let chunkData = ""; // 设置一个存储当前数组对象中数据的字符串
          let chunkJson = JSON.parse(decoder.decode(value)); // decoder.decode(编码)返回一个字符串,其中包含使用特定 TextDecoder 对象的方法解码的文本。(就是将原本变成字节流后把它变成文本)
          // 如果code不是200,说明请求失败,后面不执行
          if (chunkJson.header.code != "200") {
            return;
          }
          chunkData = JSON.parse(decoder.decode(value)).chunk;
          _this.render.contentText += chunkData; // 请求成功拼接字符串
          // reader.cancel()是reader停止请求的方法,停止请求后就不请求了(有一个停止生成的按钮,点击后就停止请求)
          // render.loading 是判断是否正在请求的loading,以此来判断是否继续递归请求后面的数据
          return _this.render.loading ? read() : reader.cancel();
        });
      }
      return read();
    })

ReadableStream 参考链接
(1)MDN ReadableStream : https://developer.mozilla.org/zh-CN/docs/Web/API/ReadableStream
(2)MDN ReadableStream getReader() 方法:https://developer.mozilla.org/zh-CN/docs/Web/API/ReadableStream/getReader
(3)MDN ReadableStream cancel() 方法:https://developer.mozilla.org/zh-CN/docs/Web/API/ReadableStream/cancel
(4)MDN TextDecoder: https://developer.mozilla.org/zh-CN/docs/Web/API/TextDecoder

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/869048.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring MVC视图解析器

Spring MVC视图解析器 ➢ AbstractCachingViewResolver&#xff1a;➢ XmlViewResolver&#xff1a;➢ ResourceBundleViewResolver➢ UrlBasedViewResolver&#xff1a;➢ InternalResourceViewResolver&#xff1a;➢ FreeMarkerViewResolver➢ ContentNegotiatingViewResolv…

OffSec Labs Proving grounds Play——FunboxEasyEnum

文章目录 端口扫描目录扫描文件上传漏洞利用查看用户爆破密码sudo提权flag位置FunboxEasyEnum writeup walkthrough Funbox: EasyEnum ~ VulnHub Enumeration Brute-force the web server’s files and directories. Be sure to check for common file extensions. Remote…

OCR的发明人是谁?

OCR的发明背景可以追溯到早期计算机科学和图像处理的研究。随着计算机技术的不断发展&#xff0c;人们开始探索如何将印刷体文字转换为机器可读的文本。 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;的发明涉及多个人的贡献&#xff0c…

布局性能优化:安卓开发者不可错过的性能优化技巧

作者&#xff1a;麦客奥德彪 当我们开发Android应用时&#xff0c;布局性能优化是一个必不可少的过程。一个高效的布局能够提高用户体验&#xff0c;使应用更加流畅、响应更加迅速&#xff0c;而低效的布局则会导致应用的运行变得缓慢&#xff0c;甚至出现卡顿、崩溃等问题&…

植被利用了多少陆地降水?

降水部分被植被利用&#xff0c;部分转化为河水流量。量化植被直接使用的水量对于解读气候变化的影响至关重要。 新提出的模型的预测与之前的结果进行了比较。资料来源&#xff1a;AGU Advances 水是地球的重要组成部分&#xff0c;因此&#xff0c;了解大尺度的水平衡及其建模…

【Rust】Rust学习 第十章泛型、trait 和生命周期

泛型是具体类型或其他属性的抽象替代。我们可以表达泛型的属性&#xff0c;比如他们的行为或如何与其他泛型相关联&#xff0c;而不需要在编写和编译代码时知道他们在这里实际上代表什么。 之后&#xff0c;我们讨论 trait&#xff0c;这是一个定义泛型行为的方法。trait 可以…

Springboot04--vue前端部分+element-ui

注意点&#xff1a; 这边v-model和value的区别&#xff1a;v-model是双向绑定的&#xff0c;value是单向绑定 li的key的问题 vue的组件化开发&#xff1a; 1. NPM&#xff08;类似maven&#xff0c;是管理前段代码的工具&#xff09; 安装完之后可以在cmd里面使用以下指令 2.…

带你认识储存以及数据库新技术演进

01经典案例 1.0 潜在问题 02存储&数据库简介 2.1 存储器层级架构 2.1 数据怎么从应用到存储介质 2.1 RAID技术 2.2 数据库 数据库分为 关系型数据库 和 非关系型数据库 2.2.2 非关系型 2.2.1 关系型 2.3 数据库 vs 经典存储-结构化数据管理 2.3.1 数据库 vs 经典存储-事务能…

c++ static

static 成员 声明为static的类成员称为类的静态成员&#xff0c;用static修饰的成员变量&#xff0c;称之为静态成员变量&#xff1b;用 static修饰的成员函数&#xff0c;称之为静态成员函数。静态成员变量一定要在类外进行初始化。 看看下面代码体会一下: //其他类 class …

​运行paddlehub报错,提示:UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte…**​

我在windows11环境下运行paddlehub报错&#xff0c;提示&#xff1a;UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte…** 参考篇文字的解决方案&#xff1a;window10下运行项目报错&#xff1a;UnicodeDecodeError: ‘gbk‘ codec can‘t decode byte...的解决办法_uni…

C语言——将一串字符进行倒序

//将一串字符进行倒序 #include<stdio.h> #define N 6 int main() {int a[N]{0,1,2,3,4,5};int i,t;printf("原数组数值&#xff1a; ");for(i0;i<N;i)printf("%d",a[i]);for(i0;i<N/2;i){ta[i];a[i]a[N-1-i];a[N-1-i]t;}printf("\n排序…

Xilinx DDR3学习总结——3、MIG exmaple仿真

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 Xilinx DDR3学习总结——3、MIG exmaple例程仿真 前言仿真 前言 前面我们直接把exmaple例程稍加修改就进行了抢先上板测试&#xff0c;证明了MIG模块工作时正常的&#xff0…

SWIG使用方法

安装 下载 swigwin软件包&#xff0c;解压到合适的位置&#xff0c;然后将路径添加到环境变量即可。 编写C代码 //vector.hpp class Vector { private:int x;int y; public:Vector(int,int);double abs();void display(); };//vector.cpp #include "vector.hpp" …

C语言 ——指针数组与数组指针

目录 一、二维数组 二、指针数组 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;书写方式 &#xff08;3&#xff09;指针数组模拟二维数组 三、数组指针 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;使用数组指针打印一维数组 &#xff08;3&a…

网络协议栈-基础知识

1、分层模型 1.1、OSI七层模型 1、OSI&#xff08;Open System Interconnection&#xff0c;开放系统互连&#xff09;七层网络模型称为开放式系统互联参考模型 &#xff0c;是一个逻辑上的定义&#xff0c;一个规范&#xff0c;它把网络从逻辑上分为了7层。 2、每一层都有相关…

【C语言】分支语句(选择结构)详解

✨个人主页&#xff1a; Anmia.&#x1f389;所属专栏&#xff1a; C Language &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 目录 什么是分支语句&#xff1f; if语句 if if - else单分支 if - else if - else ...多分支 if - if嵌套 switch语句 基本语…

HackRF One Block Diagram

HackRF One r1-r8 Block Diagram HackRF One r9 Block Diagram

FPGA 之 xilinx DDS IP相位控制字及频率控制字浅析

浅析相位环在Xilinx DDS中的理解 本文仅为个人理解之用; 相关仿真结果如下:

Qt5.9.4搭建安卓环境-Qt for Android

目录 需要安装以下内容&#xff1a;安装JDK设置环境变量安装剩余文件 使用新建文件 可能出现的问题第一种解决方法&#xff1a; 第二种解决方法 需要安装以下内容&#xff1a; 下载地址&#xff1a; https://www.qter.org/portal.php?modview&aid10 很多Qt开发会用到的环…

理解-面向对象

目录 对象&#xff1a; 举例&#xff1a; 封装: 好处: 继承: 多态&#xff1a; 类和对象之间的关系 对象&#xff1a; 把一个东西看成对象&#xff0c;我们就可以孤立的审查它的性质&#xff0c;行为&#xff0c;进而研究它和其他对象的关系。 对象是一个应用系统中用…