导语:平常开发中咱们一般会把前端的静态资源上传到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…