Skip to content

Vue.js 项目的性能优化

Published:

懒加载

路由懒加载

const routes = [
  {
    path: "/",
    name: "Home",
    // 使用 const 的动态 import 语法进行懒加载
    component: () =>
      import(/* webpackChunkName: "home" */ "../components/Home.vue"),
  },
  // 其他路由定义
];

异步组件加载

在 Vue.js@3 中,可以使用异步组件来实现按需加载。异步组件是通过 defineAsyncComponent​ 函数创建的,该函数接受一个返回 Promise​ 的函数。当组件需要渲染时,Vue.js 将执行该函数并等待 Promise​ 解析,然后加载组件。

// 异步加载的组件
const AsyncComponent = () => import("./AsyncComponent.vue");

// 使用 defineAsyncComponent 创建异步组件
const MyAsyncComponent = defineAsyncComponent(AsyncComponent);

// 在组件定义中使用异步组件
export default {
  components: {
    MyAsyncComponent,
  },
  // 其他组件配置
};

此外,可以在 defineAsyncComponent​ 中请求数据,再渲染组件:

const AsyncComp = defineAsyncComponent(() => {
  return new Promise((resolve, reject) => {
    // ...从后端获取组件
    resolve(/* 获取到的组件 */);
  });
});

甚至还可以使用高级配置去自定义:

const AsyncComp = defineAsyncComponent({
  // 加载函数
  loader: () => import("./Component.vue"),
  // 加载异步组件时使用的组件
  loadingComponent: LoadingComponent,
  // 展示加载组件前的延迟时间,默认为 200ms
  delay: 2000,
  // 加载失败后展示的组件
  errorComponent: ErrorComponent,
  // 如果提供了一个 timeout 时间限制,超时也会显示这里配置的 error 组件,默认值是:Infinity
  timeout: 3000,
});

// 在加载组件显示之前有一个默认的 200ms 延迟——这是因为在网络状况较好时,加载完成得很快,加载组件和最终组件之间的替换太快可能产生闪烁,反而影响用户感受。
// 如果提供了一个报错组件,则它会在加载器函数返回的 Promise 抛错时被渲染。

异步组件也可以同时与路由懒加载结合使用。在路由定义中,使用 component: () => import(/* chunkName: "chunk-name" */ './YourComponent.vue')​ 进行异步加载。

图片懒加载

图片懒加载可以推迟加载页面上的图片,只有在它们进入可视区域时才加载。getBoundingClientRect​ 是一个用于获取元素在视口中位置信息的方法,利用这个方法可以实现图片的懒加载。

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Lazy Loading Images</title>
    <style>
      img {
        width: 100%;
        height: auto;
        display: block;
        margin-bottom: 20px;
        transition: opacity 0.5s;
      }

      img.loading {
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div style="height: 1000px;"></div>
    <!-- Placeholder for scrolling -->

    <img data-src="image1.jpg" class="lazy-load" alt="Lazy Loaded Image 1" />
    <img data-src="image2.jpg" class="lazy-load" alt="Lazy Loaded Image 2" />
    <img data-src="image3.jpg" class="lazy-load" alt="Lazy Loaded Image 3" />
    <img data-src="image4.jpg" class="lazy-load" alt="Lazy Loaded Image 4" />

    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const lazyImages = document.querySelectorAll(".lazy-load");

        function lazyLoad() {
          lazyImages.forEach(image => {
            if (isInViewport(image) && image.classList.contains("loading")) {
              image.src = image.dataset.src;
              image.classList.remove("loading");
            }
          });
        }

        function isInViewport(element) {
          const rect = element.getBoundingClientRect();
          return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <=
              (window.innerHeight || document.documentElement.clientHeight) &&
            rect.right <=
              (window.innerWidth || document.documentElement.clientWidth)
          );
        }

        window.addEventListener("scroll", lazyLoad);
        document.addEventListener("DOMContentLoaded", lazyLoad);

        // Optional: Add a fade-in effect when images are loaded
        lazyImages.forEach(image => {
          image.addEventListener("load", () => {
            image.style.opacity = 1;
          });
        });
      });
    </script>
  </body>
</html>

此外,还有浏览器提供的 decoding="async"​ 和 loading="lazy"​ 这两个属性。decoding​ 属性用于指定浏览器在解码图片时的行为。具体来说,decoding="async"​ 告诉浏览器异步解码图像,即在图像下载完成后,浏览器会在后台进行解码,而不会阻塞页面渲染;

<img src="image.jpg" alt="Description" decoding="async" />

loading​ 属性用于控制图像的加载行为,其中 loading="lazy"​ 表示懒加载。当设置为 lazy​ 时,浏览器会推迟加载图像,直到它们进入可视范围内。

<img src="image.jpg" alt="Description" loading="lazy" />
<!-- 可以同时使用 -->
<img src="image.jpg" alt="Description" decoding="async" loading="lazy" />

不过,目前这两个属性存在着浏览器的兼容性问题,不同的浏览器对其支持程度有所不同。

资源预加载

prefetch 是一种让浏览器在空闲时间内预加载资源的指令,用于在浏览器空闲时提前加载未来可能会被导航到的资源,例如页面、脚本或样式文件。它会在浏览器后台异步地下载指定的资源,并存储在浏览器缓存中,以备将来使用。

<link rel="prefetch" href="next-page.html" />

preload​ 是一种在当前页面加载时立即加载指定资源的指令。它将关键资源放置在浏览器下载队列的优先级较高位置,以便尽早下载并应用于当前页面。通过在页面头部添加 preload​ 标签,可以确保重要的资源,如首屏所需的关键 CSS 文件、JavaScript 文件、字体文件等,会在页面渲染之前就被下载并准备就绪,从而提高页面加载性能。这样,当浏览器开始渲染页面时,这些关键资源已经存在,可以更快地完成页面渲染。

<link rel="preload" href="font.woff2" as="font" type="font/woff2" />

preconnect​ 通过指示浏览器在后续请求中预先建立到特定域名的连接,从而减少了实际资源请求时建立连接所需的时间。通过在页面中添加 preconnect​ 标签,能够告诉浏览器哪链接是重要的,从而提前建立连接,加速后续资源的加载过程。

值得注意的是,由于浏览器对同时保持的连接数量有限制,因此 preconnect​ 设置并不会一直保持,而是在必要时进行。只有在确实需要使用某个域名加载资源时,浏览器才会执行预连接操作。这样可以更有效地利用网络资源,提高页面加载速度。

<link rel="preconnect" href="https://example.com" />
<template>
  <router-link to="/page_1" prefetch>page_1</router-link>
  <router-link to="/page_2" preload>page_2</router-link>
</template>