導語:平常開發中咱們一般會把前端的靜態資源上傳到CDN,以提升訪問速度。本文將會帶領你們開發一個Webpack插件,實現把資源打包成zip,而後上傳到COS自動解壓的功能。javascript
在開發一個功能以前,咱們先來拆解一下需求。通過分析,其實須要實現的邏輯就是如下幾點:css
Webpack
構建完成以前,遍歷靜態資源,生成zipCOS(CDN)
COS
後自動解壓由於咱們上傳的是一個.zip
的文件,因此在上傳完成以後,須要對文件進行解壓。這裏會以COS觸發
的方式觸發一個雲函數,用以處理zip文件的解壓。html
一個Webpack
插件其實就是一個構造函數,原型上定義了一個apply
方法,這個方法會在Webpack啓動時被調用。因此能夠在apply
方法中設置鉤子函數,在特定的時機執行指定的邏輯。 下面就是一個Webpack
插件的基本結構:前端
class UploadToCDN {
apply (compiler) {
compiler.hooks.somehook.tap('plugin-name', (compilation) => {
// to do something
})
}
}
複製代碼
compiler
和compilation
如上面的代碼所示,在開發Webpack
插件時,必然會使用到compiler
和compilation
這兩個對象。 compiler
包含了咱們這次構建全部的配置信息,同時也提供了一些生命週期鉤子,讓咱們能夠在特定的時機執行相應的邏輯。 compilation
能夠理解爲這次運行打包的上下文,全部打包過程當中產生的結果,都會放到這個對象中。它包含了當前的模塊資源、編譯生成資源、變化的文件、以及被跟蹤依賴的狀態信息等。當Webpack
以開發模式運行時,每當檢測到一個變化,一次新的compilation
將被建立。與compiler
同樣,compilation對象也提供了不少事件回調供插件作擴展。 在UploadToCDN
插件的開發中,咱們主要用到了compiler
的兩個鉤子emit
和afterEmit
。 emit
會在輸出asset
到output
目錄以前執行,因此咱們能夠在這個鉤子函數上對生成的靜態資源進行打包壓縮。而afterEmit
會在輸出asset
到output
目錄以後執行,此時zip文件已經生成,能夠進行上傳操做。java
因此咱們的插件大概是這樣子的:node
class UploadToCDN {
constructor(options) {
this.options = options
}
apply(compiler) {
// 由於會有異步操做,因此這裏用tapAsync, 當邏輯執行完成時調用callback通知Webpack
compiler.hooks.emit.tapAsync(PLUGIN_NAME, (compilation, callback) => {
// 在資源輸出到dist以前進行壓縮
})
compiler.hooks.afterEmit.tapAsync(PLUGIN_NAME, (compilation, callback) => {
// 在資源輸出後,把dist上傳到cos
})
}
}
複製代碼
compilation.assets
這個對象上會有咱們本次構建的資源,因此咱們能夠經過遍歷獲取生成的靜態資源,同時也能夠經過給compilation.assets[filename]
賦值輸出內容。這裏咱們會使用JSZip
對文件進行壓縮。git
compiler.hooks.emit.tapAsync(PLUGIN_NAME, (compilation, callback) => {
const folder = zip.folder(this.options.filename)
// 遍歷資源,把資源放進zip包中
for (let filename in compilation.assets) {
const source = compilation.assets[filename].source()
folder.file(filename, source)
}
zip.generateAsync({ type: 'nodebuffer' }).then((content) => {
// 生成zip文件,並把文件輸出到指定目錄
this.outputPath = path.join(compilation.options.output.path, `${this.options.filename}.zip`)
const outputRelativePath = path.relative(compilation.options.output.path, this.outputPath)
compilation.assets[outputRelativePath] = new RawSource(content)
callback() // 通知Webpack該鉤子函數已經執行完畢
})
})
複製代碼
由於我使用的是騰訊雲的對象存儲,因此這裏直接經過騰訊雲的SDK
把壓縮包上傳到指定的Bucket
。github
compiler.hooks.afterEmit.tapAsync('UploadToCDN', (compilation, callback) => {
cos.putObject({
Bucket: 'your bucket',
Region: 'region',
Key: `${this.options.filename}.zip`,
StorageClass: 'STANDARD',
Body: fs.createReadStream(this.outputPath),
onProgress: progressData => {
console.log(JSON.stringify(progressData))
}
}, (err, data) => {
if (err) {
console.error('Upload to cdn fail.......!')
console.err(err)
}
console.log('Upload to cdn success...!', data)
callback()
})
})
複製代碼
上面咱們上傳的是一個zip
文件,上傳以後咱們還須要對zip
包進行解壓,讓各個靜態資源處於正確的訪問路徑上。 具體作法是:markdown
Bucket
(static
和upload
)。static
用於存放咱們真實會訪問到的靜態資源,而upload
則專門存儲咱們剛剛上傳的zip
文件。 3. 設置雲函數爲COS觸發
關於雲函數的代碼這裏就不貼出來了,由於都是基於模板生成的,基本不用本身手寫代碼。 解壓雲函數的邏輯 當有zip文件上傳到upload Bucket
時,雲函數會被觸發,同時雲函數能拿到zip文件的下載地址。以後調用SDK的API下載zip,下載完成以後進行解壓,而後遍歷目錄,把資源上傳到另外一個Bucket static
上。通過這些處理以後,咱們就能在static
目錄上訪問到咱們剛剛打包上傳的js
, html
, css
, image
等等。app
通過以上一系列邏輯實現以後,上傳CDN插件總算完成了。具體源碼請查看:github.com/samzerf/upl…