MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue懒加载 如何实现渐进式加载与预加载功能

2021-01-295.6k 阅读

Vue 懒加载基础概念

在前端开发中,随着应用规模和复杂度的提升,页面中需要加载的资源量也不断增加。图片、组件等资源如果一次性全部加载,会导致页面初始加载时间过长,影响用户体验。Vue 懒加载技术应运而生,它可以在需要的时候才加载相关资源,有效提升页面性能。

懒加载,从字面意思理解,就是延迟加载某些资源,避免在页面初始渲染时就加载所有内容。在 Vue 应用里,懒加载常见于图片加载和组件加载场景。

以图片懒加载为例,假设一个电商产品展示页面有大量商品图片,如果所有图片都在页面加载时同时请求,不仅会占用大量带宽,还可能导致页面长时间处于加载状态,用户在等待过程中可能会选择离开。通过懒加载,只有当图片进入浏览器可视区域时,才会触发图片的加载请求,这样可以大大减少页面初始加载时的请求数量和资源占用。

对于组件懒加载,在一个大型单页应用中,可能存在许多不常用的路由组件,如用户设置里一些很少被修改的高级配置组件。如果在应用启动时就加载所有路由组件,会使打包后的文件体积过大,导致首屏加载缓慢。采用懒加载方式,这些组件只有在用户实际访问对应路由时才会被加载,提高了应用的响应速度。

渐进式加载原理及实现

渐进式加载原理

渐进式加载是一种逐步加载资源的策略,主要应用于图片加载场景。其核心思想是先加载低质量、小尺寸的图片版本,随着网络条件改善或用户等待过程,再逐步替换为高质量、大尺寸的图片。

在网络环境较差的情况下,用户能够快速看到一个模糊或低分辨率的图片大致轮廓,这给用户一种页面正在加载且有内容呈现的感觉,减少等待焦虑。当网络状况变好或者用户停留时间较长时,高质量图片逐渐加载并替换低质量图片,呈现出清晰的图像。

实现方式 - 使用原生 JavaScript 结合 Vue

  1. HTML 结构
<template>
  <div>
    <img :src="lowQualitySrc" @load="handleLowQualityLoad" @error="handleError">
    <img v-if="isHighQualityLoaded" :src="highQualitySrc" class="high - quality - img">
  </div>
</template>

在上述代码中,首先展示低质量图片 lowQualitySrc,并为其添加 loaderror 事件监听器。当低质量图片加载成功后,触发 handleLowQualityLoad 方法;如果加载失败,则触发 handleError 方法。当高质量图片加载完成(通过 isHighQualityLoaded 标志判断),显示高质量图片 highQualitySrc

  1. Vue 组件逻辑
export default {
  data() {
    return {
      lowQualitySrc: 'low - quality - image.jpg',
      highQualitySrc: 'high - quality - image.jpg',
      isHighQualityLoaded: false
    };
  },
  methods: {
    handleLowQualityLoad() {
      const highQualityImg = new Image();
      highQualityImg.src = this.highQualitySrc;
      highQualityImg.onload = () => {
        this.isHighQualityLoaded = true;
      };
      highQualityImg.onerror = () => {
        console.error('High - quality image load error');
      };
    },
    handleError() {
      console.error('Low - quality image load error');
    }
  }
};

handleLowQualityLoad 方法中,创建一个新的 Image 对象,将高质量图片的 URL 赋值给它的 src 属性。当高质量图片加载成功时,设置 isHighQualityLoadedtrue,从而显示高质量图片。如果加载高质量图片出错,在控制台打印错误信息。

实现方式 - 使用 Vue - Lazyload 插件

  1. 安装插件: 在项目目录下运行 npm install vue - lazyload 安装 vue - lazyload 插件。

  2. 插件配置与使用: 在 Vue 项目的入口文件(通常是 main.js)中进行配置:

import Vue from 'vue';
import VueLazyload from 'vue - lazyload';

Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: 'error.jpg',
  loading: 'loading.gif',
  attempt: 3,
  progressive: true
});

上述配置中,preLoad 表示在距离可视区域多远时开始加载图片,error 是图片加载失败时显示的图片 URL,loading 是图片加载过程中显示的加载动画 URL,attempt 是图片加载失败后重试的次数,progressive 设置为 true 开启渐进式加载。

在模板中使用非常简单:

<template>
  <div>
    <img v - lazy="highQualitySrc">
  </div>
</template>

这里的 v - lazy 指令会自动处理图片的渐进式加载、加载失败和加载中状态,无需手动编写复杂的逻辑。

预加载原理及实现

预加载原理

预加载与懒加载不同,它是提前加载将来可能会用到的资源,当用户需要时可以直接使用,无需等待资源的加载过程。在 Vue 应用中,预加载常用于图片和组件的加载场景。

对于图片预加载,比如一个图片展示画廊应用,用户可能会依次浏览多张图片。当用户正在查看第一张图片时,应用可以提前预加载第二张图片,这样当用户点击切换到第二张图片时,图片可以立即显示,提升用户体验。

对于组件预加载,在一个多路由的 Vue 单页应用中,如果用户频繁在几个特定路由之间切换,预加载这些路由对应的组件,可以使切换过程更加流畅,减少加载延迟。

实现方式 - 图片预加载

  1. 使用原生 JavaScript 预加载图片
function preloadImages(imageUrls) {
  const promises = [];
  imageUrls.forEach(url => {
    const img = new Image();
    const promise = new Promise((resolve, reject) => {
      img.onload = resolve;
      img.onerror = reject;
      img.src = url;
    });
    promises.push(promise);
  });
  return Promise.all(promises);
}

// 使用示例
const imageUrls = ['image1.jpg', 'image2.jpg', 'image3.jpg'];
preloadImages(imageUrls).then(() => {
  console.log('All images pre - loaded successfully');
}).catch(error => {
  console.error('Image pre - load error:', error);
});

在上述代码中,preloadImages 函数接受一个图片 URL 数组。对于每个 URL,创建一个 Image 对象,并返回一个 PromisePromiseresolve 在图片加载成功时触发,reject 在图片加载失败时触发。最后,通过 Promise.all 等待所有图片预加载完成或有任何一张图片加载失败。

  1. 在 Vue 组件中使用图片预加载
<template>
  <div>
    <button @click="preloadImages">Pre - load Images</button>
    <img v - for="(url, index) in imageUrls" :key="index" :src="url">
  </div>
</template>

<script>
export default {
  data() {
    return {
      imageUrls: ['image1.jpg', 'image2.jpg', 'image3.jpg']
    };
  },
  methods: {
    preloadImages() {
      const promises = [];
      this.imageUrls.forEach(url => {
        const img = new Image();
        const promise = new Promise((resolve, reject) => {
          img.onload = resolve;
          img.onerror = reject;
          img.src = url;
        });
        promises.push(promise);
      });
      Promise.all(promises).then(() => {
        console.log('All images pre - loaded successfully');
      }).catch(error => {
        console.error('Image pre - load error:', error);
      });
    }
  }
};
</script>

在这个 Vue 组件中,提供一个按钮,点击按钮触发 preloadImages 方法,该方法实现了图片预加载逻辑。预加载完成后,在控制台打印成功信息;如果预加载失败,打印错误信息。

实现方式 - 组件预加载

  1. 使用 Vue Router 进行组件预加载: 在 Vue Router 的配置中,可以通过 beforeRouteEnter 守卫来实现组件的预加载。假设我们有两个路由组件 HomeAbout,并且希望在进入 About 路由前预加载 About 组件。

首先,定义路由配置:

import Vue from 'vue';
import Router from 'vue - router';
import Home from '@/components/Home.vue';
import About from '@/components/About.vue';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/about',
      name: 'About',
      component: () => import('@/components/About.vue')
    }
  ]
});

这里 About 组件使用了动态导入(懒加载的一种形式)。接下来,在 Home 组件中通过 beforeRouteEnter 守卫预加载 About 组件:

<template>
  <div>
    <h1>Home Page</h1>
    <router - link to="/about">Go to About</router - link>
  </div>
</template>

<script>
export default {
  beforeRouteEnter(to, from, next) {
    if (to.name === 'About') {
      import('@/components/About.vue').then(() => {
        next();
      }).catch(error => {
        console.error('Pre - load About component error:', error);
      });
    } else {
      next();
    }
  }
};
</script>

Home 组件的 beforeRouteEnter 守卫中,当即将进入的路由是 About 路由时,通过 import 提前加载 About 组件。加载成功后调用 next 继续导航;如果加载失败,在控制台打印错误信息。

  1. 使用 Webpack Prefetch 进行组件预加载: Webpack 提供了 prefetch 功能来实现资源的预加载。在 Vue 项目中,如果使用 Webpack 进行打包,可以在路由配置中使用 webpackPrefetch: true 来启用预加载。
import Vue from 'vue';
import Router from 'vue - router';
import Home from '@/components/Home.vue';
import About from '@/components/About.vue';

Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/about',
      name: 'About',
      component: () => import(/* webpackPrefetch: true */ '@/components/About.vue')
    }
  ]
});

上述代码中,webpackPrefetch: true 注释告诉 Webpack 在生成的 HTML 中添加 prefetch 相关的 <link> 标签。浏览器会在空闲时间提前下载 About 组件对应的 JavaScript 文件,当用户真正导航到 /about 路由时,组件可以更快地加载和渲染。

懒加载与预加载结合使用场景及实现

结合使用场景

在一些复杂的前端应用中,懒加载和预加载结合使用可以发挥更大的优势。例如,在一个大型的文档管理系统中,文档列表页面展示了文档的缩略图(使用懒加载),当用户将鼠标悬停在某个文档缩略图上时,预加载该文档的详细内容组件(包括高清图片、文本等)。这样,当用户点击进入文档详细页面时,内容可以快速呈现,提升用户体验。

再比如,在一个电商商品列表页面,商品图片使用懒加载,减少初始加载负担。当用户浏览到某个商品时,预加载该商品的详情页组件,包括商品描述、规格参数、相关推荐等内容,以便用户点击进入详情页时能迅速看到完整信息。

实现示例 - 以电商商品列表为例

  1. HTML 结构
<template>
  <div>
    <div v - for="(product, index) in products" :key="index" @mouseenter="preloadProductDetails(product)">
      <img v - lazy="product.thumbnail">
      <h3>{{product.name}}</h3>
    </div>
  </div>
</template>

在这个模板中,遍历商品列表,每个商品展示缩略图(使用 v - lazy 懒加载)和商品名称。当鼠标悬停在商品上时,触发 preloadProductDetails 方法预加载商品详情。

  1. Vue 组件逻辑
export default {
  data() {
    return {
      products: [
        {
          id: 1,
          name: 'Product 1',
          thumbnail: 'product1 - thumbnail.jpg',
          detailsUrl: 'product1 - details.vue'
        },
        {
          id: 2,
          name: 'Product 2',
          thumbnail: 'product2 - thumbnail.jpg',
          detailsUrl: 'product2 - details.vue'
        }
      ]
    };
  },
  methods: {
    preloadProductDetails(product) {
      import(product.detailsUrl).then(() => {
        console.log(`Pre - loaded details for product ${product.name}`);
      }).catch(error => {
        console.error(`Pre - load error for product ${product.name}:`, error);
      });
    }
  }
};

preloadProductDetails 方法中,通过 import 动态导入商品详情页组件。导入成功后,在控制台打印成功信息;如果导入失败,打印错误信息。

性能优化考量

  1. 资源优先级:无论是懒加载还是预加载,都需要考虑资源的优先级。对于用户当前操作或即将操作密切相关的资源,应给予更高的优先级。例如,在一个图片轮播应用中,当前显示图片的下一张图片预加载优先级应高于后续更远的图片。

  2. 网络状况检测:根据用户的网络状况动态调整懒加载和预加载策略。在网络较差时,减少预加载的资源数量,甚至可以降低懒加载图片的质量;在网络良好时,可以增加预加载资源数量,提高用户体验。可以使用 navigator.connection API 检测网络类型,如 wifi4g3g 等,并据此调整策略。

  3. 内存管理:预加载会提前占用一定的内存空间,如果预加载的资源过多,可能导致内存溢出。因此,需要合理控制预加载的资源数量,并且在资源不再需要时及时释放内存。例如,在一个多步骤向导式应用中,当用户完成当前步骤进入下一步后,可以释放上一步预加载但不再使用的资源。

  4. 缓存策略:对于预加载和懒加载的资源,可以结合浏览器缓存策略。如果资源已经在缓存中,直接使用缓存中的资源,避免重复请求。可以通过设置 Cache - Control 等 HTTP 头信息来控制缓存行为。

总结懒加载与预加载

懒加载和预加载是 Vue 前端开发中提升性能的重要技术手段。懒加载通过延迟加载资源,减少初始加载负担,适用于资源量较大且不是立即需要的场景;预加载则通过提前加载将来可能用到的资源,提升用户操作的响应速度。在实际应用中,根据具体业务场景合理选择和结合使用这两种技术,并充分考虑性能优化方面的因素,可以显著提升 Vue 应用的用户体验和性能表现。无论是渐进式加载的图片,还是按需加载与提前准备的组件,都是为了在用户与应用交互过程中,提供更加流畅、高效的使用感受。