gulp 的运作方式分析

Posted 全栈成长之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gulp 的运作方式分析相关的知识,希望对你有一定的参考价值。

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

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

vinyl

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

  • path:文件路径
  • contents:文件内容
  • cwd:程序执行的目录
  • base:用 glob 寻找文件时开始的目录,例如 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 封装
  contentsawait readFile(__filename),
  cwd: process.cwd(), // 不设定的话默认值为 process.cwd()
  base: process.cwd(), // 不设定的话默认值为 process.cwd()
})

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

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

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
        objectModetrue,
        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 处理的,不过我觉得这里没什么特别的东西,所以有兴趣就自己去看看吧。

关注我

我是山月,正致力于每天用五分钟能够看完的简短答案回答一个大厂高频面试题。扫码添加我的微信,备注进群,加入高级前端进阶群.

以上是关于gulp 的运作方式分析的主要内容,如果未能解决你的问题,请参考以下文章

html代码复用各种方法

社交引擎中的注册流程如何运作?调用代码的方式/内容和时间

比特币代码分析1 整体架构

gulp使用小结

gulp.spritesmith中的路径有什么问题?

高级前端以同步的方式运行 Gulp 任务和任务中的步骤