Vue 爬坑之旅 — Mint-UI 中使用 lazyload 做图片懒加载以及结合 OSS 做图片加载优化

  • Post author:
  • Post category:vue


博客已经有段时间没有更新文章了,有点惭愧,,,

因为跳到了新公司,接手他们原先的项目,老代码着实是让人不敢恭维,没有开发规范就算了,还到处都是逻辑漏洞,每次我改个什么东西基本都能发现一些其它的问题,简直是无力吐槽了。

So,接手了项目之后,要做的事情非常多,也一直没有抽出时间来总结工作中碰到的问题,今天总算是有点空余时间,赶紧撸一篇。

现在的互联网产品都离不开图片,几乎每个产品里面,图片都会有一定的占比,有的产品中,图片的占比还会非常大。所以现在的产品开发中,图片方面的优化是一个非常重要的工作。

关于图片及其优化相关的知识非常多,需要的可以自行Google,这里随便贴几篇文章


https://juejin.im/post/59a7725b6fb9a02497170459



https://www.jianshu.com/p/55e48032b30e



https://segmentfault.com/a/1190000011515334


我这里只写我实际在项目中是如何做图片优化的,相关的基础知识请自行搜索。

就前端来说,在项目中做图片优化,一个非常有效的手段就是使用懒加载的方式来加载图片,懒加载可以让页面的可操作时间提前,提高用户体验(特别是在图片很多,很大的情况下,效果非常明显)。

现在的 Vue 项目中,图片懒加载基本都是用 vue-lazyload 这个库来实现的,官方地址:

https://github.com/hilongjw/vue-lazyload#readme


插件的官方文档都写的很清楚。我按照文档所说,一顿操作之后发现怎么没起作用,这就很郁闷了,在反复检查了N遍之后,我确信我的接入方法没有问题,那么就不是插件和我的操作的问题了,问题到底出在哪呢?

在经过一段时间的思考后我突然想到,项目中有用到 Mint-UI,而 Mint-UI 好像是自带有 lazyload 功能的,于是把 Mint-UI 的文档找出来看一遍,确实有 lazyload 的功能,但是特么的文档上说的也太简单了吧,稍微高级点的用法文档上面都没说,根本满足不了实际项目的需要啊,没办法,只有靠 Google 来解决了。

一顿 Google 之后发现,特么的 Mint-UI 的 lazyload 也是用的 vue-lazyload 这个插件来实现的,所有的功能和配置都和 vue-lazyload 一模一样,但是它们的文档上只写了最简单的用法,也没说他们是用的 vue-lazyload 。更坑的是你如果同时引入了 vue-lazyload 和 Mint-UI ,你会发现 vue-lazyload 不会起作用,对此我也是很无奈。

所以在这里我对大家友情提示:

如果是新项目,千万不要用 Mint-UI,而如果不幸你接手了引入了 Mint-UI 的老项目,如果有可能,请换掉它,比它优秀的组件库有很多,真的!


不建议大家用 Mint-UI 的原因主要有二点:

  1. 文档过于简单和老旧,基本所有的组件都只给出了最简单的用法,一些高级的用法其实是支持的,但是文档上只字未提,需要自己去摸索。这样的文档我要你何用
  2. 长时间没有更新,这个库是几年前 Vue 刚火的时候推出的,那时候Mint-UI 可能是最好的组件库之一,但是现在它已经过时了,而且长时间没有更新,也存在很多的 bug

基于以上的二点,请尽量不要使用 Mint-UI,不要浪费你的时间。



Mint-UI 中 lazyload 配置

上面啰嗦了那么多,下面进入正题,如何在 Mint-UI 中对 lazyload 进行一些高级的配置,所有的配置方法都可以在 vue-lazyload 的文档上看到,只是要在引入了 Mint-UI 的项目中使用,需要绕点弯路,,,

import MintUI from 'mint-ui'

Vue.use(MintUI, {
  lazyload: {
    preLoad: 0,
    error: '/static/images/vote.png',
    loading: '/static/images/vote.png',
    attempt: 1,
    filter: {
      progressive (listener, options) {
        listener.el.setAttribute('lazy-progressive', 'true')
        // listener.loading = listener.src + '?imageView2/1/w/10/h/10'
        console.log('lazy-progressive',deviceDpr)
      },
      webp (listener, options) {
        if (options.supportWebp) {
          console.log('can use webp')
        }
      }
    },
  },
})

其实也就是在 main.js 里面全局引入 Mint-UI 后,在它后面对 lazyload 进行配置,所有的配置都能在 vue-lazyload 的文档中找到。

其实我在项目中只用到了前面四个属性,并没有用到 filter,这里说下 filter 的作用以及怎么用。filter 就是过滤器,它会在每次图片加载前对图片地址进行一次过滤,在这里可以自定义过滤规则,我能想到的一个用法就是在这里根据不同设备来加载不同分辨率的图片。

在 filter 中,可以设置多个方法,也可以只写一个,方法的多少并没有区别,每次图片加载时会依次执行 filter 中的方法。(这是我试出来的,不知道是不是真的是这样,,,)

一开始我也是打算在 filter 中对图片 URL 进行过滤并加上阿里 OSS 参数的,后来发现我们项目中图片类型很多,不同的类型需要不同的参数规则,在 filter 中写不太合适,所以就采用了另外的方式来设置 OSS 参数。



用 OSS 来做到图片按需加载

OSS :就是对象存储,图片存储是其中的一项服务,目前各大云服务提供商基本都有这方面的服务。

我的设想是在项目中,根据不同设备的 dpr 来加载不同分辨率的图片,以达到缩短图片加载时间,加快页面响应速度,提升用户体验的目的。

要达到这个目的,可以在上面的 lazylaod 的 filter 中进行控制,但是我们的项目中要加载的图片类型有好几种,在 filter 中统一处理不太方便,我就封装了一个函数挂载到 String 的原型上,通过传入要加载的图片的类型来进行 OSS 规则的设置,代码如下:

/**
 * 图片的阿里 oss 路径,根据当前设备的 dpr 返回不同后缀的路径
 * deviceDpr 为定义在 index.html 中的一个全局变量
 * @param type 传入字符串,现共有四种类型:avatar,banner,thumb,thumb-md
 * @returns {string}
 */
String.prototype.ossImg = function (type) {
  let url = this
  if (myUtils.isNull(url)) {
    return ''
  }
  
  //根据设备的 dpr 和图片要显示的位置设置不同的请求参数
  switch (type) {
    case 'banner':
      if (deviceDpr < 1.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_375,h_213/quality,q_80'
      } else if (1.5 <= deviceDpr < 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_563,h_320/quality,q_85'
      } else if (deviceDpr >= 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_750,h_425/quality,q_85'
      }
      break
    case 'thumb':
      if (deviceDpr < 1.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_127,h_72/quality,q_80'
      } else if (1.5 <= deviceDpr < 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_190,h_108/quality,q_85'
      } else if (deviceDpr >= 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_254,h_144/quality,q_85'
      }
      break
    case 'thumb-md':
      if (deviceDpr < 1.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_164,h_92/quality,q_80'
      } else if (1.5 <= deviceDpr < 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_246,h_138/quality,q_85'
      } else if (deviceDpr >= 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_328,h_184/quality,q_85'
      }
      break
    case 'avatar':
      if (deviceDpr < 1.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_44,h_44/quality,q_80'
      } else if (1.5 <= deviceDpr < 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_66,h_66/quality,q_85'
      } else if (deviceDpr >= 2.5) {
        url = url.replace('http:', '') + '?x-oss-process=image/auto-orient,1/resize,m_fill,w_88,h_88/quality,q_85'
      }
      break
    default:
      url = url.replace('http:', '')
      break
  }
  
  return url
}

这里顺便说一个知识点,在上面对 URL 处理的代码中,有这么一段

url.replace('http:', '')

可能有些人不知道这段代码是干嘛的(老代码中这么写的,我开始也是不知道,,,),这段代码的意思是将 URL 中的 http 给去掉,那为什么要去掉呢,去掉了还能正常加载吗?

首先,去掉 HTTP 是因为我们的项目中有些图片 URL 是 HTTP 开头的,而后来改版升级了就改为了 https 的,这样就导致了新老图片地址请求协议不同的问题。为了解决这个问题就把老图片的 http 给去掉了。

那去掉了还能正常加载吗,答案是肯定的,当把图片的 http 去掉后,它在加载时会根据你站点的请求方式来自动给自己加上请求协议头,比如你的站头是用 http 访问的,那么它就会自动给加上 http,如果是 https 访问,那么就是 https,这是浏览器给处理的,所以完全不用担心图片加载不出来的问题。

话说这个知识点是有点生僻啊,我在见到这样的处理方式之前是完全没有听过的。。。孤陋寡闻了,,,

以上就是我实际运用到项目中的图片加载优化方面的实践,如有什么不对的地方欢迎指正,如有什么更好的实践方式,也希望能不吝赐教。



版权声明:本文为zgh0711原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。