实现一个自动上传资源到CDN的Webpack插件

导语:平常开发中咱们一般会把前端的静态资源上传到CDN,以提升访问速度。本文将会带领你们开发一个Webpack插件,实现把资源打包成zip,而后上传到COS自动解压的功能。javascript

需求分析

在开发一个功能以前,咱们先来拆解一下需求。通过分析,其实须要实现的逻辑就是如下几点:css

  • Webpack构建完成以前,遍历静态资源,生成zip
  • 把zip上传到COS(CDN)
  • 上传到COS后自动解压

由于咱们上传的是一个.zip的文件,因此在上传完成以后,须要对文件进行解压。这里会以COS触发的方式触发一个云函数,用以处理zip文件的解压。html

Webpack插件的基本结构

一个Webpack插件其实就是一个构造函数,原型上定义了一个apply方法,这个方法会在Webpack启动时被调用。因此能够在apply方法中设置钩子函数,在特定的时机执行指定的逻辑。 下面就是一个Webpack插件的基本结构:前端

class UploadToCDN {
    apply (compiler) {
      compiler.hooks.somehook.tap('plugin-name', (compilation) => {
        // to do something
      })
    }
  }
复制代码

compilercompilation

如上面的代码所示,在开发Webpack插件时,必然会使用到compilercompilation这两个对象。 compiler包含了咱们这次构建全部的配置信息,同时也提供了一些生命周期钩子,让咱们能够在特定的时机执行相应的逻辑。 compilation能够理解为这次运行打包的上下文,全部打包过程当中产生的结果,都会放到这个对象中。它包含了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息等。当Webpack以开发模式运行时,每当检测到一个变化,一次新的compilation将被建立。与compiler同样,compilation对象也提供了不少事件回调供插件作扩展。 在UploadToCDN插件的开发中,咱们主要用到了compiler的两个钩子emitafterEmitemit会在输出assetoutput目录以前执行,因此咱们能够在这个钩子函数上对生成的静态资源进行打包压缩。而afterEmit会在输出assetoutput目录以后执行,此时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该钩子函数已经执行完毕
    })
  })
复制代码
  • 上传zip文件

由于我使用的是腾讯云的对象存储,因此这里直接经过腾讯云的SDK把压缩包上传到指定的Bucketgithub

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

  1. 建立两个Bucket(staticupload)。static用于存放咱们真实会访问到的静态资源,而upload则专门存储咱们刚刚上传的zip文件。
  2. 选择模板建立云函数,函数模板选择Nodejs解压zip包。

3. 设置云函数为COS触发 关于云函数的代码这里就不贴出来了,由于都是基于模板生成的,基本不用本身手写代码。 解压云函数的逻辑 当有zip文件上传到upload Bucket时,云函数会被触发,同时云函数能拿到zip文件的下载地址。以后调用SDK的API下载zip,下载完成以后进行解压,而后遍历目录,把资源上传到另外一个Bucket static上。通过这些处理以后,咱们就能在static目录上访问到咱们刚刚打包上传的js, html, css, image等等。app

通过以上一系列逻辑实现以后,上传CDN插件总算完成了。具体源码请查看:github.com/samzerf/upl…

相关文章
相关标签/搜索