沐鳴下載_分析 gulp 的運作方式

要說到 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 封裝
  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 設計的本意是要處理大文件的,它能一次讀取文件的一小部分,然後再傳給調用者進行處理:

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 處理的,不過我覺得這裏沒什麼特別的東西,所以有興趣就自己去看看吧。

    站長推薦

    1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

    2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

    鏈接: http://www.fly63.com/article/detial/10023