流式构建工具,自动化加强你的工作流
- 基于任务配置
- 提供插件系统
- 简单、定义良好的API
- 基于
nodejs stream
设计,前端友好,构建高效。
gulp
处理流程:
- 启动一个
task
, - 通过
glob
语法定位处理的文件流 - 通过插件 进行插件内容的处理
- 最终产出输出文件。
定位要处理的文件 - Glob
几个特殊的字符
* Matches 0 or more characters in a single path portion
? Matches 1 character
[...] Matches a range of characters, similar to a RegExp range. If the first character of the range is ! or ^ then it matches any character not in the range.
!(pattern|pattern|pattern) Matches anything that does not match any of the patterns provided.
?(pattern|pattern|pattern) Matches zero or one occurrence of the patterns provided.
+(pattern|pattern|pattern) Matches one or more occurrences of the patterns provided.
*(a|b|c) Matches zero or more occurrences of the patterns provided
@(pattern|pat*|pat?erN) Matches exactly one of the patterns provided
** If a "globstar" is alone in a path portion, then it matches zero or more directories and subdirectories searching for matches. It does not crawl symlinked directories.
- 一个
*
, 表示匹配文件名中 0个或大于0个的任意字符。 - ? 代表一个任意的字符
- 两个
*
, 表示匹配文件夹名 0个或大于0个的任意字符。 - {xxx,aaa} 表示匹配文件夹名是xxx或者aaa
- [] 类似正则表达式中的 范围
- !(pattern|pattern|pattern) 除了pattern匹配的其他字符
具体参考:node-glob
实际使用:
// 匹配当前文件夹下的所有已`.js`结尾的文件
*.js
./*.js
// 匹配当前文件夹下的所有的已`test`开头的文件
test*
// 匹配当前文件夹下的所有的已`test`开头的js文件
test*.js
// 匹配当前文件夹下的所有的已`test`开头的js文件
test*.js
// 匹配当src文件夹下的所有的已`test`开头,间隔一个字符,后加一个aa或者是bb文件
src/test?{aa,bb}*
// 匹配当src文件夹下的所有的已`test`开头,间隔一个字符,后面不是`aa` `nice`的所有文件
src/test-[^(aa)(nice)]*
// 匹配当前文件夹以及子目录的所有的已`test`开头的js文件
**/test-*.js
vinyl File objects
gulp
依赖的一个模块。
stream
为啥需要stream
?
假设下面的代码:
const http = require('http');
const fs = require('fs');
// 响应一个大的文件给客户端
const server = http.createServer((req, res)=> {
fs.readFile('bigfile.txt', 'utf-8', function (err, data) {
if(err) throw err;
res.end(data)
})
});
server.listen(3000);
这个时候的简单处理流程是这样的:
- 客户端发起请求
- 服务端接收请求
- 将文件全部一次性读取到内存中,然后通过网络接口发送到客户端
这里的问题在于:一次性的读取到内存中。
我们知道计算机发展到今天,硬件已经有了很大的发展进步,但是即使这样我们的普通的计算机内存也是很有限的(一般是4-8g),对于服务器而言也不会很大,内存要求越高,价格也越高。如果我们读取的是一个几百兆的文件,加上并发,服务器将变得异常脆弱,很容易因为内存占用过高,导致应用无法提供正常的服务。
那么stream
是什么
四种流:
-
reaableStream
提供数据源(能够从中读取数据) -
writableStream
写入数据源(能够写入数据) -
duplexStream
双工(既可以当做数据源 也可写入数据) transformStream
使用stream
- 使用
pipe
方法
reableStream.pipe(writableStream)
reableStream
.pipe(duplexStream)
.pipe(duplexStream)
.pipe(writableStream)
-
通过事件使用
image.png
# readable.pipe(writable)
readable.on('data', (chunk) => {
writable.write(chunk);
});
readable.on('end', () => {
writable.end();
});
实现stream
- 实现一个
reable stream
const inStream = new Readable({
read(size) {
this.push(String.fromCharCode(this.currentCharCode++));
if (this.currentCharCode > 90) {
this.push(null);
}
}
});
inStream.currentCharCode = 65;
inStream.pipe(process.stdout);
- 实现一个
writable stream
const { Writable } = require('stream');
const outStream = new Writable({
write(chunk, encoding, callback) {
console.log(chunk.toString());
callback();
}
});
process.stdin.pipe(outStream);
- 实现一个
duplex stream
,同时实现read
write
方法
const { Duplex } = require('stream');
const inoutStream = new Duplex({
write(chunk, encoding, callback) {
console.log(chunk.toString());
callback();
},
read(size) {
this.push(String.fromCharCode(this.currentCharCode++));
if (this.currentCharCode > 90) {
this.push(null);
}
}
});
inoutStream.currentCharCode = 65;
process.stdin.pipe(inoutStream).pipe(process.stdout);
- 实现一个
transform stream
, 实现transform
方法
const { Transform } = require('stream');
const upperCaseTr = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
process.stdin.pipe(upperCaseTr).pipe(process.stdout);
stream
的objectMode
-
nodejs
创建的所有的流对象,只能操作string
和buffer
以及Buffer
对象。 - 提供了一个配置项:
objectMode
那么stream
是怎么解决的呢?
不会一次将文件内容读取到内存中,而是分批次的进行处理。先写入到缓存区Buffer
,然后等待消费
关于Buffer
- 可读流和可写流都会把数据存储内部的一个缓存区。
-
highWaterMark
控制缓存区的大小 - 当可读流调用
stream.push(data)
的时候 会把data缓存到Buffer
中 - 如果消费这个流的消费者没有触发
strea.read()
,data就一直在内部队列中,等待被消费。 - 一旦内部读取缓冲区的总大小达到highWaterMark指定的阈值,流将暂时停止从基础资源读取数据,直到可以使用当前缓冲的数据为止
- 重复调用
writable.write(chunk)
方法时,数据将缓存在可写流中 - 当内部写缓冲区的总大小低于
highWaterMark
设置的阈值时,对writable.write()
的调用将返回true
- 一旦内部缓存区大小达到或者超过了
highWaterMark
设置的阈值,writable.write()
将返回false
参考:
- https://www.freecodecamp.org/news/node-js-streams-everything-you-need-to-know-c9141306be93/
- https://github.com/substack/stream-handbook
插件体系
实现Transform 流
function gulpRename(obj, options) {
var stream = new Stream.Transform({ objectMode: true });
stream._transform = function(originalFile, unused, callback) {
// code
// transformFunction 处理你的原文件
let file = transformFunction(originalFile);
callback(null, file);
}
return stream;
}
参考:
- https://github.com/gulpjs/gulp/blob/master/docs/writing-a-plugin/README.md
- https://nodejs.org/api/stream.html#stream_class_stream_transform_1
- https://v3.gulpjs.com.cn/docs/writing-a-plugin/
- https://github.com/gulpjs/gulp/blob/master/docs/writing-a-plugin/README.md
- https://www.gulpjs.com.cn/docs/getting-started/explaining-globs/
网友评论