前端页面性能优化:WEBP + VUE实践

5,317 阅读2分钟

本文旨在如何快速在vue项目上切换全站图片,并针对不兼容的浏览器版本进行降级。

生成webp

对于目前大多数的项目来说,比较方便且合适的方式是集成webpack的loader或者plugin,在build的过程中生成webp。当然也可以预先在项目中预先准备好webp,比如说在UI给切图时,多提供一个webp格式。

目前已有很多loader和plugin来干这个事情,本文中使用的是imagemin-webp-webpack-plugin,配置很简单,样例如下:

import ImageminWebpWebpackPlugin from 'imagemin-webp-webpack-plugin'

...

plugins: [
  ...
  new ImageminWebpWebpackPlugin({
    config: [
      {
        test: /\.(jpe?g|png)/,
        options: {
          quality: 75
        }
      }
    ],
    overrideExtension: false
  })
]

这边为了后面降级方便,所以overrideExtension设置为false,意思是 image.png -> image.png.webp

如果这时一切正常的话,你已经获得了一份原始图片和一份webp格式的图片了,如下图:

vue中应用

webp在项目中实际应用,已经有人总结过很多方法了,比如使用<picture>、nginx拦截等等,既然本文是webp+vue实践,则推荐使用vue-lazyload。 这个组件有很多功能,这里主要讲下如何使用它来快速接入webp。

先引用进来:

import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload)

直接把src替换成v-lazy

// 原先的写法
<img class="top-image" src="./images/banner.png" />

// vue-lazyload的写法
<img class="top-image" v-lazy="./images/banner-new.png" />

然后,直接npm start或者build,问题来了:./images/banner-new.png并没有和之前一样,被转成实际的地址。

在vue-loader文档里确认了下:

在模板编译过程中,编译器可以将某些特性转换为 require 调用,例如 src 中的 URL。因此这些目标资源可以被 webpack 处理。例如 <img src="./foo.png"> 会找到你文件系统中的 ./foo.png 并将其作为一个依赖包含在你的包里。

{
  video: ['src', 'poster'],
  source: 'src',
  img: 'src',
  image: ['xlink:href', 'href'],
  use: ['xlink:href', 'href']
}

在经过一系列骚操作无效后,只能进行曲线救国:

// vue-lazyload的写法
<img v-lazy class="top-image" temp="./images/banner-new.png" />
// 配置vue-loader的transformAssetUrls属性
{
  loader: 'vue-loader',
  options: {
    transformAssetUrls: {
      img: ['src', 'temp']
    }
  }
}
import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload, {
  filter: {
    progressive(listener, options) {
      // 把temp的值赋给src
      listener.src = $(listener.el).attr('temp')
    }
  }
})

现在,总算可以正常加载图片了,还差最后一步。

import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload, {
  filter: {
    progressive(listener, options) {
      // 把temp的值赋给src
      listener.src = $(listener.el).attr('temp')
    },
    webp(listener, options) {
   	  // 1. 是否支持webp
      // 2. 考虑编译速度及热更新相关问题,DEV环境还是照旧
      // 3. 如果是svg的话,照旧
      // 4. 做一个防错误处理,是cdn的地址才走webp
      if (options.supportWebp && !__DEV__ && !listener.src.endsWith('.svg') && listener.src.startsWith(cdnSetting[__ENV__].publicPath)) {
        listener.src += '.webp'
      }
    }
  }
})

webp判断可以根据自己的情况进行调整。

最后,写一个正则全量替换项目中的<img />

TODO

总结

大致就是这样,如有问题,可以回复沟通讨论,谢谢观看。