【前端性能优化实践】手把手教你实现webpack图片压缩插件

前言

我想写一个系列:前端性能优化实践方案。网上虽然一搜一大把这样的文章,但大多缺乏体系化。也有很多讲性能优化的书籍,但其实想照着书上的知识进行实践,还是挺难的一件事。
这是该系列的第一篇文章
由于自己的知识水平有限,文章中难免有出错的地方,如果你看到,望指正。
由于刚开始写作,文章风格和写作方式还比较青涩,如果你有任何建议,可以直接向我反馈,感谢Thanks♪(・ω・)ノ

背景

最近在做页面性能优化相关工作,发现项目中很多图片尺寸都比较大(>1M),虽然使用了webp图片方案,但对于不支持webp图片格式的设备来说,图片资源加载慢的问题并没有得到有效解决。针对这个问题,有两种解决方案,一是让设计同学重新给小尺寸的图;二是自己压缩图片。方案一治标不治本,且对不同的项目无法实现复用,故这里采用第二种方案。

压缩图片采用的是 https://tinypng.com/ 方式进行压缩,它单次最大支持20张图片的压缩个数,如果项目图片比较少,可以直接进行手动压缩;由于我优化的项目中图片个数达到了上百张,因此,写了webpack插件来提升开发效率,同时解决了单次压缩个数的限制。

具体实现

在讲具体实现之前,我这里先说一下自己的实现思路,我会按照实现思路依次讲解实现步骤【需要了解webpack插件实现原理】:
1.获取项目构建中的图片资源
2.将获取到的图片资源依次上传到 https://tinypng.com/ 官网进行压缩
3.获取压缩后的图片资源,替换原有图片

1.获取项目构建的图片资源

// 兼容不同版本的webpack
apply(compiler) {
    // webpack verison >= 4
    if (compiler.hooks && this._options.compress) {
      const isWebpack4 = compiler.webpack ? false : typeof compiler.resolvers !== 'undefined';
      if (isWebpack4) {
        compiler.hooks.emit.tapPromise(pluginName, compilation => {
          // handleImgAssets 方法具体处理图片资源
          return Promise.resolve(this.handleImgAssets(compilation))
        })
      } else { // webpack 5
        compiler.hooks.compilation.tap(pluginName, compilation => {
          compilation.hooks.processAssets.tapPromise(pluginName, () => {
            return Promise.resolve(this.handleImgAssets(compilation))
          })
        })
      }
    } else if (compiler.plugin && this._options.compress) {
      // webpack3的写法
      compiler.plugin('emit', async (compilation, callback) => {
        await this.handleImgAssets(compilation)
        callback()
      })
    } else {
      console.log(`The webpack version number supported by ${pluginName} is 3-5!, install: https://webpack.js.org/`)
    }
  }

通过上面的代码,我们就可以拿到compilation对象,图片资源就在这个对象里面,我们把它作为参数传给handleImgAssets方法,具体处理细节在handleImgAssets中。接下来,让我们看看它具体干了些啥。

2.将获取到的图片资源依次上传到 https://tinypng.com/ 官网进行压缩

async handleImgAssets(compilation) {
    // 通过 assets 字段获取所有的静态资源
    const ImgAssets = compilation.assets
    // 过滤出图片
    let images = Object.keys(compilation.assets).filter(asset => IMG_TEST.test(asset))
    // 如果图片不存在,直接返回
    if (!images.length) {
      return Promise.resolve()
    }
    // 插件支持自定义压缩图片的尺寸区间,这里就是找出真正需要压缩的图片
    // 比如我设置了 大于 10kb的图片才进行压缩,这里会过滤出尺寸大于10kb的图
    images = this.filterImages(images, ImgAssets)
    // 我们将每个图片压缩过程转成promise
    // 具体实现在 compressImg 方法里
    const imgPromises = images.map(img => this.compressImg(ImgAssets, img))
    const spinner = Ora('Compressing Image......').start()
    // 并发控制
    await this.promiseLimit(imgPromises, this._options.concurrency).then(res => {
      spinner.stop()
      this._options.log && res && res.forEach(msg => console.log(msg))
    })
  }

3.获取压缩后的图片资源,替换原有图片

/** 处理图片压缩工作 */
compressImg(assets, key) {
    try {
      // 获取图片的原始资源
      const file = assets[key].source()
      // 上传图片资源,进行压缩。具体实现在uploadImg方法里
      const originData = await this.uploadImg(file)
      // 获取压缩后的图片资源
      const compressedData = await this.downloadImg(originData.output.url)
      // 替换原资源
      assets[key] = new RawSource(Buffer.alloc(compressedData.length, compressedData, 'binary'))
      return new Promise((resolve, reject) => resolve(msg))
    } catch (error) {
      // do something
    }
  }

/**上传图片方法,接口会自动进行压缩 */ 
  uploadImg(source) {
    const header = DefaultHeader()
    return new Promise((resolve, reject) => {
      const req = Https.request(header, res => res.on('data', data => {
        const resObj = JSON.parse(data.toString())
        // 如果报错,直接reject
        // 如果成功,resolve 接口返回的对象,里面包含压缩后的图片地址,需要再次请求去下载
        resObj.error ? reject(resObj.message) : resolve(resObj)
      }))
      req.write(source, 'binary')
      req.on('error', e => reject(e))
      req.end()
    })
  }

/** 下载压缩后的图片 */
  downloadImg(url) {
    // 这里就是拿到刚刚接口返回的地址,再次请求,下载图片资源就好
    const URL = new Url.URL(url)
    return new Promise((resolve, reject) => {
      const req = Https.request(URL, res => {
        let file = ''
        res.setEncoding('binary')
        res.on('data', data => file += data)
        res.on('end', () => resolve(file))
      })
      req.on('error', e => reject(e))
      req.end()
    })
  }

以上就完成了图片压缩工作,完整的代码可查看这里。该插件在项目打包的过程中,实现图片压缩功能,当然,如果压缩后的效果设计同学不满意,我们去掉压缩即可,不会对原有图片资源造成污染。

我将上述插件发布成了npm包,想使用的同学可以直接安装使用。目前公司内部很多项目都在使用,整体图片资源大小降低了40%以上,页面加载速度和用户体验也得到了明显的提升。

结尾

目前只支持线上压缩功能,如果你的工作环境没有网,那该插件不能正常工作。后期想通过canvas来实现离线压缩,感兴趣的同学可以一起搞一下~ヾ(◍°∇°◍)ノ゙【你将会成为共同作者】

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了优化前端性能,Webpack可以采取多种措施。首先,通过代码压缩、合并和缓存控制,可以减少文件大小并提高加载速度。例如,可以使用Webpack压缩插件(如UglifyJS)来压缩JavaScript代码,并使用提取公共代码的功能来减少重复代码的加载。这样可以减小文件体积并提升页面加载速度。 其次,Webpack还支持使用ES6或CoffeeScript等高级语言来编写源码,并将其构建成浏览器支持的ES5代码,从而提高开发效率和代码质量。这可以通过使用Babel等工具和Webpack的loader来实现。 此外,Webpack还支持CSS的压缩。可以使用插件(如css-minimizer-webpack-plugin)来去除无用的空格,减小CSS文件的大小。这样可以提高页面的加载速度。 最后,对于HTML文件,Webpack也可以进行压缩。可以使用插件(如html-minimizer-webpack-plugin)来去除HTML文件中的无用空格、注释等,从而减小文件大小。这样可以加快页面加载速度。 综上所述,通过使用Webpack的各种优化手段,可以有效地提升前端项目的性能,加快页面加载速度,提高用户体验。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【前端知识之webpackwebpack如何优化前端性能](https://blog.csdn.net/weixin_44337386/article/details/125845074)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [webpack学习程之前端性能优化总结](https://download.csdn.net/download/weixin_38656364/14902051)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值