了解Node.js中的流(Stream)

2023-05-16

Node.js 中的流(Stream)是出了名的难用甚至是难以理解。

用 Dominic Tarr 的话来说:“流是 Node 中最好的,也是最容易被误解的想法。”即使是 Redux 的创建者和 React.js 的核心团队成员 Dan Abramov 也害怕 Node 流。

1.png

本文将帮助你了解流以及如何使用。不要害怕,你完全可以把它搞清楚!

什么是流(Stream)?

流(Stream)是为 Node.js 应用提供动力的基本概念之一。它们是数据处理方法,用于将输入的数据顺序读取或把数据写入输出。

流是一种以有效方式处理读写文件、网络通信或任何类型的端到端信息交换的方式。

流的处理方式非常独特,流不是像传统方式那样将文件一次全部读取到存储器中,而是逐段读取数据块并处理数据的内容,不将其全部保留在内存中。

这种方式使流在处理大量数据时非常强大,例如,文件的大小可能大于可用的内存空间,从而无法将整个文件读入内存进行处理。那是流的用武之地!

既能用流来处理较小的数据块,也可以读取较大的文件。

以 YouTube 或 Netflix 之类的“流媒体”服务为例:这些服务不会让你你立即下载视频和音频文件。取而代之的是,你的浏览器以连续的块流形式接收视频,从而使接收者几乎可以立即开始观看和收听。

但是,流不仅涉及处理媒体和大数据。它们还在代码中赋予了我们“可组合性”的力量。考虑可组合性的设计意味着能够以某种方式组合多个组件以产生相同类型的结果。在 Node.js 中,可以通过流在其他较小的代码段中传递数据,从而组成功能强大的代码段。

为什么使用流?

与其他数据处理方法相比,流基本上具有两个主要优点:

  1. 内存效率:你无需事先把大量数据加载到内存中即可进行处理
  2. 时间效率:得到数据后立即开始处所需的时间大大减少,不必等到整个有效数据全部发送完毕才开始处理

Node.js 中有 4 种流:

  1. 可写流:可以向其中写入数据的流。例如,fs.createWriteStream() 使我们可以使用流将数据写入文件。
  2. 可读流:可从中读取数据的流。例如:fs.createReadStream() 让我们读取文件的内容。
  3. 双工流(可读写的流):可读和可写的流。例如,net.Socket
  4. Transform:可在写入和读取时修改或转换数据。例如在文件压缩的情况下,你可以在文件中写入压缩数据,也可以从文件中读取解压缩的数据。

如果你已经使用过 Node.js,则可能遇到过流。例如在基于 Node.js 的 HTTP 服务器中,request 是可读流,而 response 是可写流。你可能用过 fs 模块,该模块可让你用可读和可写文件流。每当使用 Express 时,你都在使用流与客户端进行交互,而且由于 TCP 套接字、TLS栈和其他连接都基于 Node.js,所以在每个可以使用的数据库连接驱动的程序中使用流。

实例

如何创建可读流?

首先需要可读性流,然后将其初始化。

const Stream = require('stream')
const readableStream = new Stream.Readable()

现在,流已初始化,可以向其发送数据了:

readableStream.push('ping!')
readableStream.push('pong!')

异步迭代器

强烈建议在使用流时配合异步迭代器(async iterator)。根据 Axel Rauschmayer 博士的说法,异步迭代是一种用于异步检索数据容器内容的协议(这意味着当前“任务”可以在检索项目之前被暂停)。另外必须提及的是,流异步迭代器实现使用内部的 readable 事件。

从可读流中读取时,可以使用异步迭代器:

import * as fs from 'fs';

async function logChunks(readable) {
  for await (const chunk of readable) {
    console.log(chunk);
  }
}

const readable = fs.createReadStream(
  'tmp/test.txt', {encoding: 'utf8'});
logChunks(readable);

// Output:
// 'This is a test!\n'

也可以用字符串收集可读流的内容:

import {Readable} from 'stream';

async function readableToString2(readable) {
  let result = '';
  for await (const chunk of readable) {
    result += chunk;
  }
  return result;
}

const readable = Readable.from('Good morning!', {encoding: 'utf8'});
assert.equal(await readableToString2(readable), 'Good morning!');

注意,在这种情况下必须使用异步函数,因为我们想返回 Promise。

请切记不要将异步功能与 EventEmitter 混合使用,因为当前在事件处理程序中发出拒绝时,无法捕获拒绝,从而导致难以跟踪错误和内存泄漏。目前的最佳实践是始终将异步函数的内容包装在 try/catch 块中并处理错误,但这很容易出错。 这个 pull request 旨在解决一旦其落在 Node 核心上产生的问题。

要了解有关异步迭代的 Node.js 流的更多信息,请查看这篇很棒的文章。

Readable.from():从可迭代对象创建可读流

stream.Readable.from(iterable, [options]) 这是一种实用方法,用于从迭代器中创建可读流,该迭代器保存可迭代对象中包含的数据。可迭代对象可以是同步可迭代对象或异步可迭代对象。参数选项是可选的,除其他作用外,还可以用于指定文本编码。

const { Readable } = require('stream');

async function * generate() {
  yield 'hello';
  yield 'streams';
}

const readable = Readable.from(generate());

readable.on('data', (chunk) => {
  console.log(chunk);
});

两种读取模式

根据 Streams API,可读流有效地以两种模式之一运行:flowingpaused。可读流可以处于对象模式,无论处于 flowing 模式还是 paused 模式。

  • 流模式下,将自动从底层系统读取数据,并通过 EventEmitter 接口使用事件将其尽快提供给程序。
  • 在 paused 模式下,必须显式调用 stream.read() 方法以从流中读取数据块。

在 flowing 模式中,要从流中读取数据,可以监听数据事件并附加回调。当有大量数据可用时,可读流将发出一个数据事件,并执行你的回调。看下面的代码片段:

var fs = require("fs");
var data = '';

var readerStream = fs.createReadStream('file.txt'); //Create a readable stream

readerStream.setEncoding('UTF8'); // Set the encoding to be utf8. 

// Handle stream events --> data, end, and error
readerStream.on('data', function(chunk) {
   data += chunk;
});

readerStream.on('end',function() {
   console.log(data);
});

readerStream.on('error', function(err) {
   console.log(err.stack);
});

console.log("Program Ended");

函数调用 fs.createReadStream() 给你一个可读流。最初流处于静态状态。一旦你侦听数据事件并附加了回调,它就会开始流动。之后将读取大块数据并将其传递给你的回调。流实现者决定发送数据事件的频率。例如,每当有几 KB 的数据被读取时,HTTP 请求就可能发出一个数据事件。当从文件中读取数据时,你可能会决定读取一行后就发出数据事件。

当没有更多数据要读取(结束)时,流将发出结束事件。在以上代码段中,我们监听此事件以在结束时得到通知。

另外,如果有错误,流将发出并通知错误。

在 paused 模式下,你只需在流实例上重复调用 read(),直到读完所有数据块为止,如以下示例所示:

var fs = require('fs');
var readableStream = fs.createReadStream('file.txt');
var data = '';
var chunk;

readableStream.on('readable', function() {
    while ((chunk=readableStream.read()) != null) {
        data += chunk;
    }
});

readableStream.on('end', function() {
    console.log(data)
});

read() 函数从内部缓冲区读取一些数据并将其返回。当没有内容可读取时返回 null。所以在 while 循环中,我们检查是否为 null 并终止循环。请注意,当可以从流中读取大量数据时,将会发出可读事件。

所有 Readable 流均以 paused 模式开始,但可以通过以下方式之一切换为 flowing 模式

  • 添加一个 'data' 事件处理。
  • 调用 stream.resume() 方法。
  • 调用 stream.pipe() 方法将数据发送到可写对象。

Readable 可以使以下方法之一切换回 paused 模式:

  • 如果没有管道目标,则通过调用 stream.pause() 方法。
  • 如果有管道目标,请删除所有管道目标。可以通过调用 stream.unpipe() 方法来删除多个管道目标。

一个需要记住的重要概念是,除非提供了一种用于消耗或忽略该数据的机制,否则 Readable 将不会生成数据。如果使用机制被禁用或取消,则 Readable 将会试图停止生成数据。添加 readable 事件处理会自动使流停止 flowing,并通过 read.read() 得到数据。如果删除了 readable 事件处理,那么如果存在 'data' 事件处理,则流将再次开始 flowing。

如何创建可写流?

要将数据写入可写流,你需要在流实例上调用 write()。如以下示例所示:

var fs = require('fs');
var readableStream = fs.createReadStream('file1.txt');
var writableStream = fs.createWriteStream('file2.txt');

readableStream.setEncoding('utf8');

readableStream.on('data', function(chunk) {
    writableStream.write(chunk);
});

上面的代码很简单。它只是简单地从输入流中读取数据块,并使用 write() 写入目的地。该函数返回一个布尔值,指示操作是否成功。如果为 true,则写入成功,你可以继续写入更多数据。如果返回 false,则表示出了点问题,你目前无法写任何内容。可写流将通过发出 drain 事件来通知你什么时候可以开始写入更多数据。

调用 writable.end() 方法表示没有更多数据将被写入 Writable。如果提供,则可选的回调函数将作为 finish 事件的侦听器附加。

// Write 'hello, ' and then end with 'world!'.
const fs = require('fs');
const file = fs.createWriteStream('example.txt');
file.write('hello, ');
file.end('world!');
// Writing more now is not allowed!

你可以用可写流从可读流中读取数据:

const Stream = require('stream')

const readableStream = new Stream.Readable()
const writableStream = new Stream.Writable()

writableStream._write = (chunk, encoding, next) => {
    console.log(chunk.toString())
    next()
}

readableStream.pipe(writableStream)

readableStream.push('ping!')
readableStream.push('pong!')

writableStream.end()

还可以用异步迭代器来写入可写流,建议使用

import * as util from 'util';
import * as stream from 'stream';
import * as fs from 'fs';
import {once} from 'events';

const finished = util.promisify(stream.finished); // (A)

async function writeIterableToFile(iterable, filePath) {
  const writable = fs.createWriteStream(filePath, {encoding: 'utf8'});
  for await (const chunk of iterable) {
    if (!writable.write(chunk)) { // (B)
      // Handle backpressure
      await once(writable, 'drain');
    }
  }
  writable.end(); // (C)
  // Wait until done. Throws if there are errors.
  await finished(writable);
}

await writeIterableToFile(
  ['One', ' line of text.\n'], 'tmp/log.txt');
assert.equal(
  fs.readFileSync('tmp/log.txt', {encoding: 'utf8'}),
  'One line of text.\n');

stream.finished() 的默认版本是基于回调的,但是可以通过 util.promisify() 转换为基于 Promise 的版本(A行)。

在此例中,使用以下两种模式:

Writing to a writable stream while handling backpressure (line B):
在处理 backpressure 时写入可写流(B行):

if (!writable.write(chunk)) {
  await once(writable, 'drain');
}

关闭可写流,并等待写入完成(C行):

writable.end();
await finished(writable);

pipeline()

pipeline(管道)是一种机制,可以将一个流的输出作为另一流的输入。它通常用于从一个流中获取数据并将该流的输出传递到另一个流。管道操作没有限制。换句话说,管道可用于分多个步骤处理流数据。

在 Node 10.x 中引入了 stream.pipeline()。这是一种模块方法,用于在流转发错误和正确清理之间进行管道传输,并在管道完成后提供回调。

这是使用管道的例子:

const { pipeline } = require('stream');
const fs = require('fs');
const zlib = require('zlib');

// 使用 pipeline API 可以轻松将一系列流
// 通过管道传输在一起,并在管道完全完成后得到通知。
// 一个有效地用 gzip压缩巨大视频文件的管道:

pipeline(
  fs.createReadStream('The.Matrix.1080p.mkv'),
  zlib.createGzip(),
  fs.createWriteStream('The.Matrix.1080p.mkv.gz'),
  (err) => {
    if (err) {
      console.error('Pipeline failed', err);
    } else {
      console.log('Pipeline succeeded');
    }
  }
);

由于pipe 不安全,应使用 pipeline 代替 pipe

流模块

Node.js 流模块 提供了构建所有流 API 的基础。

Stream 模块是 Node.js 中默认提供的原生模块。 Stream 是 EventEmitter 类的实例,该类在 Node 中异步处理事件。因此流本质上是基于事件的。

要访问流模块:

const stream = require('stream');

stream 模块对于创建新型流实例非常有用。通常不需要使用 stream 模块来消耗流。

流驱动的 Node API

由于它们的优点,许多 Node.js 核心模块提供了原生流处理功能,最值得注意的是:

  • net.Socket 是流所基于的主 API 节点,它是以下大多数 API 的基础
  • process.stdin 返回连接到 stdin 的流
  • process.stdout 返回连接到 stdout 的流
  • process.stderr 返回连接到 stderr 的流
  • fs.createReadStream() 创建一个可读的文件流
  • fs.createWriteStream() 创建可写的文件流
  • net.connect() 启动基于流的连接
  • http.request() 返回 http.ClientRequest 类的实例,它是可写流
  • zlib.createGzip() 使用gzip(一种压缩算法)将数据压缩到流中
  • zlib.createGunzip() 解压缩 gzip 流。
  • zlib.createDeflate() deflate(压缩算法)将数据压缩到流中
  • zlib.createInflate() 解压缩一个deflate流

流 备忘单:

2.png

3.png

4.png

5.png

6.png

查看更多:Node.js 流速查表

以下是与可写流相关的一些重要事件:

  • error –表示在写或配置管道时发生了错误。
  • pipeline – 当把可读流传递到可写流中时,该事件由可写流发出。
  • unpipe – 当你在可读流上调用 unpipe 并停止将其输送到目标流中时发出。

结论

这就是所有关于流的基础知识。流、管道和链是 Node.js 的核心和最强大的功能。流确实可以帮你编写简洁而高效的代码来执行 I/O。

另外,还有一个值得期待的 Node.js 战略计划,称为 BOB,旨在改善 Node.js 的内部数据流以及希望作为未来 Node.js 流数据接口的公共 API 的。

英文原文地址:https://nodesource.com/blog/understanding-streams-in-nodejs

更多web前端开发知识,请查阅 HTML中文网 !!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解Node.js中的流(Stream) 的相关文章

  • 将文本流从 NodeJS 传递到浏览器

    我正在尝试将 NodeJS 中处理的文本文件流式传输到浏览器 以下是处理前的文本文件 该文件名为 dbUsers json userId 443 email email protected cdn cgi l email protectio
  • 使用 TcpClient 和反应式扩展从 Stream 读取连续字节流

    考虑以下代码 internal class Program private static void Main string args var client new TcpClient client ConnectAsync localhos
  • 计算流数据的直方图 - 在线直方图计算

    我正在寻找一种算法来生成大量流数据的直方图 最大值和最小值事先未知 但标准差和平均值在特定范围内 我很欣赏你的想法 Cheers 我刚刚找到了一个解决方案 秒 从流式并行决策树算法构建在线直方图 论文的 2 2 该算法由 Hive 项目中的
  • c# 模拟 IFormFile CopyToAsync() 方法

    我正在对一个异步函数进行单元测试 该函数将 IFormFile 列表转换为我自己的任意数据库文件类列表 将文件数据转换为字节数组的方法是 internal async Task
  • node.js - 将两个可读流写入同一个可写流

    我想知道如果您同时将两个不同的读取流传输到同一目的地 node js 会如何操作 例如 var a fs createReadStream a var b fs createReadStream b var c fs createWrite
  • 使用 mkfifo 和传输流,这可能吗?

    我想执行一个 bash 脚本来执行以下操作 应用程序 ffmpeg 生成实时传输流 ts 文件 我需要处理这个实时流 执行解复用等 现在我知道这必须通过 FIFO 来完成 但这是我的任务 我需要重定向 ffmpeg 的输出以写入 fifo
  • 是否可以像 html 视频元素一样将流作为源添加到 html canvas 元素?

    根据MDN The HTMLMediaElement https developer mozilla org en US docs Web API HTMLMediaElement界面添加到HTMLElement属性 支持基本媒体相关功能所
  • 是否可以使用 Dapper 流式传输大型 SQL Server 数据库结果集?

    我需要从数据库返回大约 500K 行 请不要问为什么 然后 我需要将这些结果保存为 XML 更紧急 并将该文件通过 ftp 传输到某个神奇的地方 我还需要转换结果集中的每一行 现在 这就是我正在做的事情 TOP 100结果 使用 Dappe
  • 如何从 C++ std::basic_ostream 派生并使 << 运算符虚拟?

    我正在编写一个具有各种消息输出的类 我想让这个类变得通用并且独立于平台 所以我正在考虑通过一个基本流引用它 它可以将所有消息转储到流中 通过这样做 如果该类在控制台程序中使用 我可以通过std cout并显示在控制台窗口中 或者我可以将派生
  • 使用 node.js 上的 knox 将八位字节流从请求流式传输到 S3

    我正在尝试使用以下命令将八位字节流直接流式传输到 S3knox https github com LearnBoost knox 在 Node js 上 octet stream 是从浏览器上传的 XHR 文件 我以为我可以将请求流式传输到
  • 如何在Python 3中将文本流编码为字节流?

    将字节流解码为文本流很容易 import io f io TextIOWrapper io BytesIO b Test nTest n utf 8 f readline 在这个例子中 io BytesIO b Test nTest n 是
  • 使用任何节点模块在内存中创建 ZIP 文件

    是否有任何节点模块可以在内存中创建 zip 我不想将 zip 文件保存在磁盘上 以便我们可以将这个创建的 zip 文件发送到其他服务器 从内存 做这个的最好方式是什么 这是我的例子 var file system require fs va
  • 如何使用 Spring Boot 传输音频

    我想让用户能够播放声音 我的实现在 Firefox 上运行良好 在 Safari 上 不播放声音 我验证了音频控制可以在 Safari 中与其他网站一起使用 所以 我认为我必须更改控制器中的某些内容 控制器 RequestMapping v
  • 读取 GetResponseStream() 的最佳方式是什么?

    从 GetResponseStream 读取 HTTP 响应的最佳方法是什么 目前我正在使用以下方法 Using SReader As StreamReader New StreamReader HttpRes GetResponseStr
  • C# List 处置/关闭

    我正在设置订阅服务 以便按计划向我们公司的各个人员发送报告 我计划通过电子邮件发送报告 我使用的报告系统能够导出为 PDF 流 而不是写入临时文件 大多数人会收到不止一份报告 因此我尝试将它们全部附加到一封电子邮件中 执行以下操作 List
  • Scala Stream 按需要调用(惰性)与按名称调用

    所以我知道按需要呼叫只是按名称呼叫的记忆版本 在 Martin Odersky 在 Coursera 上的 FP 课程中 第 7 3 讲 惰性评估 中 他提到如果 Streams 是使用按名称调用来实现的 那么它可能会导致计算复杂性的激增
  • 如何组合输出流,以便输出同时到达多个位置?

    我想将两个 或更多 流合成为一个 我的目标是任何输出都指向cout cerr and clog也可以与原始流一起输出到文件中 例如 当事情记录到控制台时 关闭后 我仍然希望能够返回并查看输出 我正在考虑做这样的事情 class stream
  • 在 Python 中使用音频流 RTMP 通过管道和 OpenCV 到 FFmpeg

    我正在尝试使用音频流式传输 FFmpeg 我将在下面展示我的代码 导入模块 import subprocess as sp 创建变量 rtmpUrl rtmp a rtmp youtube com live2 key camera path
  • Child_process 处理带有回车符 (\r) 的 STDOUT 流

    我正在编写一个简单的应用程序 它允许工作中的内部系统请求从远程服务器到使用 REST 调用发起的另一个远程服务器的复制过程 使用 rsync 我已经对express框架足够熟悉 并且刚刚开始尝试child process库 并偶然发现了一个
  • 如何将 MemoryStream 写入 byte[] [重复]

    这个问题在这里已经有答案了 可能的重复 从流创建字节数组 https stackoverflow com questions 221925 creating a byte array from a stream 我正在尝试在内存中创建文本文

随机推荐

  • 聊聊JavaScript作用域

    几乎所有的语言都有作用域的概念 xff0c 简单的说 xff0c 作用域就是变量和函数的可访问范围 xff0c 即作用域控制在变量和函数的可见性和生命周期 变量作用域 是程序源代码中定义这个变量的区域 1 全局变量拥有全局作用域 xff0c
  • CSS布局方案大全【整理】

    我们在日常开发中经常遇到布局问题 xff0c 下面罗列几种常用的css布局方案话不多说 xff0c 上代码 xff01 居中布局 以下居中布局均以不定宽为前提 xff0c 定宽情况包含其中 1 水平居中 a inline block 43
  • 你必须知道的一些HTML优化技巧

    如何提升Web页面的性能 xff0c 很多开发人员从多个方面来下手如JavaScript 图像优化 服务器配置 xff0c 文件压缩或是调整CSS 很显然HTML 已经达到了一个瓶颈 xff0c 尽管它是开发Web 界面必备的核心语言 HT
  • 常见的CSS图形绘制合集

    展示40多个常见的纯CSS绘制图形 xff0c 效果实时 xff0c 含源代码 xff0c 想要什么效果直接复制粘贴就好了 就算项目用不到 xff0c 看看别人代码怎么写的也有助于提高CSS的基本功 以下这些造型简单的图形都是纯CSS外加一
  • 扩展Vue.js组件

    您的Vue应用程序中是否有共享类似选项的组件 xff0c 甚至模板标记 使用公共选项和标记创建基本组件 xff0c 然后扩展基本组件以创建子组件 xff0c 这是一个好主意 这样的体系结构将帮助您在代码中应用DRY原则 不要重复您自己 xf
  • Node.js日志记录指南

    当你开始用 JavaScript 进行开发时 xff0c 可能学到的第一件事就是如何用 console log 将内容记录到控制台 如果你去搜索如何调试 JavaScript xff0c 会发现数百篇博文和 StackOverflow 文章
  • 软件测试的测试方法及测试流程

    一 测试方法 xff1a 白盒测试 xff1a 把软件比作一个打开的盒子 xff0c 可以看到软件代码的实现 xff0c 针对代码的实现验证代码是否存在问题 单元测试阶段采用的测试方法 灰盒测试 xff1a 介于白盒和黑盒测试之间 灰盒测试
  • 18个用于创建漂亮图表的JavaScript库推荐

    几乎不可能想象没有图形和图表的任何仪表盘 它们快速有效地呈现复杂的统计数据 此外 xff0c 一个好的图表还可以增强网站的整体设计 在本文中 xff0c 我将向您展示一些用于图形和图表的最佳JavaScript库 这些库将帮助您为将来的项目
  • JavaScript基础--引用数据类型 (对象)

    本文主要讲述了JavaScript对象的属性和对象的引用 xff0c 以及对象的读取 赋值 删除和获取对象键名的操作 1 对象 对象就是一组 键值对 xff08 key value xff09 的集合 xff0c 是一种无序的复合数据集合
  • javascript面向对象设计

    javascript和java语言不一样 xff0c 它没有类这个说法 xff0c 更没有子类父类一说 xff0c 所以它所谓的继承机制 xff0c 也是和其他语言完全不同的 创建对象三种方式 1 最简单的方式 xff0c 创建一个obje
  • 关键CSS和Webpack:自动最小化渲染阻止CSS

    消除渲染阻止的JavaScript和CSS 这是我始终坚持使用的Google Page Speed Insights建议 Google建议您在页面加载准备就绪时将最初需要的CSS 内插 CSS内嵌并加载CSS的其余部分 当访问web页面时
  • 如何用SASS编写可重用的CSS

    Sass是一个CSS预处理程序 xff0c 它在前端工程师的工具箱中变得至关重要 Sass之所以流行 xff0c 是因为它修复了几个CSS缺陷 相关推荐 xff1a css在线手册 这也是Bootstrap 4运行的基础 这意味着为了理解如
  • JS 变量的作用域及闭包

    与闭包有关的概念 xff1a 变量的作用域和变量的生存周期 下面本篇文章就来给大家介绍一下JS中变量的作用域及闭包 xff0c 有一定的参考价值 xff0c 有需要的朋友可以参考一下 xff0c 希望对大家有所帮助 一 变量的作用域 1 变
  • 理解JavaScript中的变量、范围和提升

    变量是许多编程语言的基本组成部分 xff0c 也是新手需要学习的第一个也是最重要的概念 JavaScript中有许多不同的变量属性 xff0c 以及命名变量时必须遵循的一些规则 在JavaScript中 xff0c 有三个关键字用于声明变量
  • 用于VueJS和Webpack的代码分割模式

    拆分单个页面应用程序的代码是提高其初始加载速度的一个很好的方法 由于用户不必一次性下载所有代码 xff0c 他们将能够更快地看到页面并与页面进行交互 这将提高用户体验 xff0c 特别是在移动领域 xff0c 这是搜索引擎优化的一个胜利 x
  • javascript掌握正则表达式

    正则表达式 xff08 英语 xff1a Regular Expression xff0c 在代码中常简写为regex regexp或RE xff09 使用单个字符串来描述 匹配一系列符合某个句法规则的字符串搜索模式 我想正则表达式之所以难
  • 知道要测试什么——Vue组件单元测试

    关于单元测试Vue组件 xff0c 我看到的最常见的问题是 我到底应该测试什么 虽然测试过多或过少都是可能的 xff0c 但我的观察是 xff0c 开发人员通常会在测试过多时犯错误 毕竟 xff0c 没有人愿意成为一个组件测试不足导致应用程
  • 软件测试 | 测试开发书单 | 测试工程师必读经典好书,你读过几本?

    测试好书1080 480 46 3 KB 软件测试入行容易进阶难 在持续交付体系背景下 xff0c 要成为测试开发高手意味着非常系统综合的知识储备 广泛阅读经典好书是快速成长的必要方式 霍格沃兹测试学院重点推荐几本测试经典好书以及必读清单
  • 使用webpack提升vue.js应用程序的四种方法

    Webpack是开发Vue js单页面应用程序的必要工具 通过管理复杂的构建步骤 xff0c 它使您的开发工作流更加简单 xff0c 并且可以优化应用程序的大小和性能 在本文中 xff0c 我将解释Webpack提升Vue应用程序的四种方法
  • 了解Node.js中的流(Stream)

    Node js 中的流 xff08 Stream xff09 是出了名的难用甚至是难以理解 用 Dominic Tarr 的话来说 xff1a 流是 Node 中最好的 xff0c 也是最容易被误解的想法 即使是 Redux 的创建者和 R