gulp 的运作方式分析

// 每日前端夜话 第457篇
// 正文共:1100 字
// 预计阅读时间:6 分钟

说到 gulp 的运作方式,就不得不提到 vinyl 和 Node.js 的 stream。

vinyl 是 gulp 所使用的虚拟的文件格式,在它的自述文件是这么说的:“当提到文件时你首先想到的是什么?肯定是路径和内容吧”,它主要记录的信息有:

  • path
    :文件路径
  • contents
    :文件内容
  • cwd
    :程序执行的目录
  • base
    src/**/*.js
    base
    src
    

另外它还有几个函数用来判断这个文件的内容是什么类型的这类操作,到于这个虚拟文件实际上用在什么地方,咱们稍后再说,先创建一个文件试试:

const { readFile } = require('fs/promises')
const Vinyl = require('vinyl')

const file = new Vinyl({
path: __filename, // 这个文件的路径
// 在 Node.js 14.8.0 中 支持 top level await,不过还是建议用 async function 封装
contents: await readFile(__filename),
cwd: process.cwd(), // 不设定的话默认值为 process.cwd()
base: process.cwd(), // 不设定的话默认值为 process.cwd()
})

console.log(file) // 这是应该能看到 <File "file.js" <Buffer ...>>

这样就创建好了一个 gulp 用的文件格式,接下来是 Node.js 的 stream。

stream 设计的本意是要处理大文件的,它能一次读取文件的一小部分,然后再传给调用者进行处理:

const { createReadStream } = require('fs')

// 创建一个读取文件的 stream
const stream = createReadStream(__filename)

// 设置文件字符集编码,否则就会以 Buffer (二进制数据) 的格式读取
stream.setEncoding('utf-8')

// 处理数据
stream.on('data', (chunk) => {
console.log('chunk', JSON.stringify(chunk))
})

// 结束
stream.on('end', () => {
console.log('end')
})

实际上它是基于 EventEmitter 之上创建的一组 API,比如 on
就是来自于 EventEmitter,只要照着它的模式,也不一定只能传小块的文件,在 Node.js 中的 stream 也有一个对象模式,如果传的数据不是缓冲区或流就应该设置为对象模式,而对象模式跟一般的模式主要的区别就是不需要处理字符集编码。

再回到 gulp,还记得之前说过 src
是回传一个 stream 吗?接下来看看里面到底传的是什么,先写个 gulpfile 来试看看:

exports.stream = function () {
const stream = src('./src/**.js')
stream.on('data', (data) => {
console.log(data)
})
return stream
}

输出:

<File "file.js" <Buffer ...>>

没错,这就是 Vinyl 的文件,gulp 用 stream 的对象模式在传输这些文件,plugin 其实上就是回传一个 Transform
的 stream(Node.js 中 stream 的一种,stream 的种类有 Readable、Writeable、Duplex、Transform)来转换这些文件,比下面是一个把文件内容都换成大写的流:

const { Transform } = require('stream')

exports.uppercase = function () {
return src('./src/**.js')
.pipe(
new Transform({
// 设置为 object mode
objectMode: true,
transform(file, _enc, cb) {
// 把文件内容转换成为字符串
const content = file.contents.toString()
// 转为大写后再转回 Buffer 存回去
file.contents = Buffer.from(content.toUpperCase())
// 用 callback 回传
cb(null, file)
},
})
)
.pipe(dest('upper'))
}

现在我们有了一个把文件全转大写的 plugin了,不过没什么实用上的意义。这样转来转去的效率太低了。

剩下的部分就是 gulp 处理任务的注册与依赖性的逻辑了,依赖性主要是由 undertaker
处理的,不过我觉得这里没什么特别的东西,所以有兴趣就自己去看看吧。

强力推荐前端面试刷题神器

精彩文章回顾,点击直达

点分享

点收藏

点点赞

点在看

前端先锋
我还没有学会写个人说明!
上一篇

nodejs+express 最简易的连接数据库

下一篇

分享一次批量文件翻译的开发过程

你也可能喜欢

评论已经被关闭。

插入图片