分析 gulp 的運做方式

要說到 gulp 的運做方式,就不得不提到 vinyl 和 Node.js 的 streamjavascript

vinyl

vinyl 是 gulp 用的虛擬的檔案格式,在它的 readme 是這麼說的:「說你檔案你第一個想到的是什麼?路徑跟內容吧」,它主要紀錄的資訊有:前端

vinyl 是 gulp 所使用的虛擬的文件格式,在它的自述文件是這麼說的:「當提到文件時你首先想到的是什麼?確定是路徑和內容吧」,它主要記錄的信息有:java

  • path:文件路徑
  • contents:文件內容
  • cwd:程序執行的目錄
  • base:用 glob 尋找文件時開始的目錄,例如 src/**/*.js,那 base 就會是 src,這能夠用來重現目錄結構

另外它還有幾個函數用來判斷這個文件的內容是什麼類型的這類操做,到於這個虛擬文件實際上用在什麼地方,我們稍後再說,先建立一個文件試試:git

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

stream 設計的本意是要處理大文件的,它能一次讀取文件的一小部分,而後再傳給調用者進行處理:github

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 來試看看:gulp

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

輸出:segmentfault

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

沒錯,這就是 Vinyl 的文件,gulp 用 stream 的對象模式在傳輸這些文件,plugin 其實上就是回傳一個 Transform 的 stream(Node.js 中 stream 的一種,stream 的種類有 Readable、Writeable、Duplex、Transform)來轉換這些文件,比下面是一個把文件內容都換成大寫的流:promise

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 處理的,不過我以爲這裏沒什麼特別的東西,因此有興趣就本身去看看吧。

173382ede7319973.gif


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章


歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索