0


webpack plugin源码解析(六) CompressionWebpackPlugin

文章目录

作用

  • 压缩打包后的文件,可以配置是否删除源文件
const CompressionPlugin =require("compression-webpack-plugin");newCompressionPlugin()

涉及 webpack API

  • 处理 asset 钩子compilation.hooks.processAssets- PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER:优化已有 asset 的转换操作阶段,例如对 asset 进行压缩,并作为独立的 asset- additionalAssets: true 会多次调用回调,一次是在指定 stage 添加资产时触发回调,另一次是后来由插件添加资产时,这里为 CompressionWebpackPlugin 添加的压缩文件后触发

compiler.hooks.thisCompilation.tap(pluginName,compilation=>{
  compilation.hooks.processAssets.tapPromise({name: pluginName,// 优化已有 asset 的转换操作,例如对 asset 进行压缩,并作为独立的 assetstage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER,additionalAssets:true// true会多次调用回调,一次是在指定 stage 添加资产时触发回调,另一次是后来由插件添加资产时},assets=>this.compress(compiler, compilation, assets));});
  • 返回或新建缓存:compilation.getCache- 具体查看 copy-webpack-plugin 解析文章

  • 返回 asset 文件信息:compilation.getAsset

const{
     info,
     source
   }=compilation.getAsset(name);// name:"main.js" 打包后输出文件的 name
  • 文件名匹配函数:compiler.webpack.ModuleFilenameHelpers.matchObject- 具体查看 copy-webpack-plugin 解析文章

  • 模版字符串替换:compilation.getPath- 具体查看 copy-webpack-plugin 解析文章

实现

constructor

  • 初始化选项和压缩配置,以及默认使用 zlib 库进行压缩
classCompressionPlugin{constructor(options){validate(/** @type {Schema} */
    schema, options ||{},{name:"Compression Plugin",baseDataPath:"options"});const{
      test,
      include,
      exclude,
      algorithm ="gzip",
      compressionOptions ={},
      filename =(options ||{}).algorithm ==="brotliCompress"?"[path][base].br":"[path][base].gz",
      threshold =0,
      minRatio =0.8,
      deleteOriginalAssets =false}= options ||{};this.options ={
      test,
      include,
      exclude,
      algorithm,
      compressionOptions,
      filename,
      threshold,
      minRatio,
      deleteOriginalAssets
    };/**
    {
      test: undefined,
      include: undefined,
      exclude: undefined,
      algorithm: "gzip",
      compressionOptions: {
        level: 9,
      },
      filename: "[path][base].gz",
      threshold: 0,
      minRatio: 0.8,
      deleteOriginalAssets: false,
    }
    */this.algorithm =this.options.algorithm;if(typeofthis.algorithm ==="string"){const zlib =require("zlib");// 默认使用 zlib 压缩this.algorithm = zlib[this.algorithm];if(!this.algorithm){thrownewError(`Algorithm "${this.options.algorithm}" is not found in "zlib"`);}const defaultCompressionOptions ={gzip:{level: zlib.constants.Z_BEST_COMPRESSION// 9},deflate:{level: zlib.constants.Z_BEST_COMPRESSION},deflateRaw:{level: zlib.constants.Z_BEST_COMPRESSION},brotliCompress:{params:{[zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY}}}[algorithm]||{};this.options.compressionOptions ={// 传递给 zlib 的压缩参数...defaultCompressionOptions,...this.options.compressionOptions
      };}}}

apply

  • 通过 processAssets 钩子的 PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER 阶段进行 assets 压缩
apply(compiler){const pluginName =this.constructor.name;
   
   compiler.hooks.thisCompilation.tap(pluginName,compilation=>{
     compilation.hooks.processAssets.tapPromise({name: pluginName,// 优化已有 asset 的转换操作,例如对 asset 进行压缩,并作为独立的 assetstage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER,additionalAssets:true// true会多次调用回调,一次是在指定 stage 添加资产时触发回调,另一次是后来由插件添加资产时,这里为 CompressionWebpackPlugin 添加的压缩文件后触发},assets=>this.compress(compiler, compilation, assets));

     compilation.hooks.statsPrinter.tap(pluginName,stats=>{
       stats.hooks.print.for("asset.info.compressed").tap("compression-webpack-plugin",(compressed,{
         green,
         formatFlag
       })=> compressed ?green(formatFlag("compressed")):"");});});}

compress

  • 遍历源 asset 进行压缩,会通过缓存已压缩文件来优化性能

asset 数据结构
在这里插入图片描述

asynccompress(compiler, compilation, assets){const cache = compilation.getCache("CompressionWebpackPlugin");// 遍历文件const assetsForMinify =(await Promise.all(Object.keys(assets).map(asyncname=>{// 获取文件信息const{
        info,
        source
      }=compilation.getAsset(name);})if(info.compressed){// 当插件第一次添加压缩文件后,因为 additionalAssets:true 会第二次触发插件回调,如果第一次被压缩了 info.compressed 为 truereturnfalse;}// 通过开发者传递的 test、exclude、include 匹配文件if(!compiler.webpack.ModuleFilenameHelpers.matchObject.bind(undefined,this.options)(name)){returnfalse;}// 获取压缩相关 namelet relatedName;// "gzipped"if(typeofthis.options.algorithm ==="function"){if(typeofthis.options.filename ==="function"){
        relatedName =`compression-function-${crypto.createHash("md5").update(serialize(this.options.filename)).digest("hex")}`;}else{/**
         * @type {string}
         */let filenameForRelatedName =this.options.filename;const index = filenameForRelatedName.indexOf("?");if(index >=0){
          filenameForRelatedName = filenameForRelatedName.slice(0, index);}

        relatedName =`${path.extname(filenameForRelatedName).slice(1)}ed`;}}elseif(this.options.algorithm ==="gzip"){
      relatedName ="gzipped";}else{
      relatedName =`${this.options.algorithm}ed`;}if(info.related && info.related[relatedName]){returnfalse;}// 缓存文件相关const cacheItem = cache.getItemCache(serialize({// 第一个参数key:序列化成字符串,通过 serialize-javascript 库序列化成字符串
      name,algorithm:this.options.algorithm,compressionOptions:this.options.compressionOptions
    }), cache.getLazyHashedEtag(source));// 第二个参数 etag: 根据资源文件内容生成 hash// 返回缓存内容const output =(await cacheItem.getPromise())||{};// 返回文件 bufferlet buffer;// No need original buffer for cached filesif(!output.source){if(typeof source.buffer ==="function"){
       buffer = source.buffer();}// Compatibility with webpack plugins which don't use `webpack-sources`// See https://github.com/webpack-contrib/compression-webpack-plugin/issues/236else{
       buffer = source.source();if(!Buffer.isBuffer(buffer)){// eslint-disable-next-line no-param-reassign
         buffer = Buffer.from(buffer);}}if(buffer.length <this.options.threshold){// 小于开发者传入的要压缩的阈值退出returnfalse;}}return{
     name,
     source,
     info,
     buffer,
     output,
     cacheItem,
     relatedName
    };}))).filter(assetForMinify=>Boolean(assetForMinify));// webpack 格式文件,用于生成输出文件 const{
    RawSource
  }= compiler.webpack.sources;const scheduledTasks =[];// 压缩操作for(const asset of assetsForMinify){
      scheduledTasks.push((async()=>{// ...})}await Promise.all(scheduledTasks);}

生成输出压缩文件

// 压缩操作for(const asset of assetsForMinify){
      scheduledTasks.push((async()=>{const{
          name,
          source,
          buffer,
          output,
          cacheItem,
          info,
          relatedName
        }= asset;// 优先将压缩相关内容存入缓存if(!output.source){if(!output.compressed){try{// 文件内容压缩
              output.compressed =awaitthis.runCompressionAlgorithm(buffer);}catch(error){
              compilation.errors.push(error);return;}}// 压缩效果相关阈值,> 开发者传入的值跳过if(output.compressed.length / buffer.length >this.options.minRatio){await cacheItem.storePromise({compressed: output.compressed
            });return;}// 根据压缩后的内容生成文件
          output.source =newRawSource(output.compressed);await cacheItem.storePromise(output);// 存入 source、compressed}// this.options.filename:"[path][base].gz" , filename:"main.css"// newFilename:'main.css.gz'const newFilename = compilation.getPath(this.options.filename,{filename: name // name:"main.css"});const newInfo ={compressed:true};// 是否删除源文件,通过 compilation.updateAsset 更新源文件信息if(this.options.deleteOriginalAssets){if(this.options.deleteOriginalAssets ==="keep-source-map"){
            compilation.updateAsset(name, source,{// @ts-ignorerelated:{sourceMap:null}});}

          compilation.deleteAsset(name);}else{
          compilation.updateAsset(name, source,{related:{[relatedName]: newFilename
            }});}// 生成压缩文件
        compilation.emitAsset(newFilename, output.source, newInfo);})}

runCompressionAlgorithm

  • 通过 zlib 进行压缩
const zlib =require("zlib");this.algorithm = zlib['gzip'];runCompressionAlgorithm(input){returnnewPromise((resolve, reject)=>{this.algorithm(input,this.options.compressionOptions,(error, result)=>{if(error){reject(error);return;}if(!Buffer.isBuffer(result)){resolve(Buffer.from(result));}else{resolve(result);}});});}

本文转载自: https://blog.csdn.net/weixin_43294560/article/details/130246421
版权归原作者 神奇大叔 所有, 如有侵权,请联系我们删除。

“webpack plugin源码解析(六) CompressionWebpackPlugin”的评论:

还没有评论