Vite+Vue3 加载速度优化

发布时间 2023-10-18 16:32:52作者: acheding

可以考虑从以下几个方面优化。整体思路:1.减小打包体积。2.异步加载。

静态资源拆分打包

在常规打包方法下,所有的第三方依赖将会都打包在一个 vendor.js 文件里,首次打开页面时,服务器会先加载这个大文件,导致白屏时间过长。

而我们打包时,事先将依赖拆分成很多小文件各自进行打包,便可以实现异步加载,大大降低加载时长,提升加载效率,减少白屏时间。具体操作如下:

export default defineConfig({
  plugins: [vue()],
  build: {
    chunkSizeWarningLimit: 1000, // 单个模块文件大小限制1000KB
    terserOptions: {
      // 清除代码中console和debugger
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 静态资源拆分
          if (id.includes("node_modules")) {
            return id
              .toString()
              .split("node_modules/")[1]
              .split("/")[0]
              .toString();
          }
        },
      },
    },
  },
});

nginx 开启 gzip 压缩

gzip 压缩分两种,一种是服务器压缩后传输给浏览器,但是这种方案是请求时服务器实时压缩,比较消耗服务器性能;另外一种就是前端在打包的时候压缩好,服务器做一些相应配置就可以返回压缩包给浏览器,只是打包出来的 dist 体积会偏大,但是不消耗服务器性能。这两种综合起来使用是比较划算的。

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	listen 443 ssl default_server;
	listen [::]:443 ssl default_server;

	root /xxxx/xxxxxx/xxxxx/dist;

	index index.html;

	server_name xxxxx.xxxx;

    gzip on; // 开启服务器实时gzip
    gzip_static on; // 开启静态gz文件返回,如果存在请求以gz结尾的静态文件,则直接返回该文件
    gzip_min_length 1k; // 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    #gzip_disable "msie6"; // 禁用IE 6 gzip
    gzip_vary on; // 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_proxied any; // 对代理文件进行压缩
    gzip_comp_level 9; // gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间
    gzip_buffers 4 16k; // 设置压缩所需要的缓冲区大小
    gzip_http_version 1.0; // 设置gzip压缩针对的HTTP协议版本
    // 进行压缩的文件类型
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/jpeg image/gif image/png image/svg+xml;

	location / {
		try_files $uri $uri/ /index.html;
	}
}

部分依赖不打包,引入 cdn

在没有使用 cdn 加速之前,例如 element-plus、vue、pinia、axios、vue-router、echarts 等依赖都会打包,导致 vendor.js 体积很大,影响首屏加载速度。

这个优化则是选择性地将这些第三方依赖不加入打包,部署到线上之后直接引用线上 cdn 地址。

1.首先需要下载一个插件。

yarn add rollup-plugin-external-globals -D

2.然后在 main.js 文件中使用此插件,进行选择性打包,并在打包时将本地的引用替换成 cdn。这样就可以做到在本地仍然使用依赖,部署线上使用 cdn。

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import externalGlobals from "rollup-plugin-external-globals";

export default defineConfig({
  plugins: [vue()],
  build: {
    chunkSizeWarningLimit: 1000,
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    },
    rollupOptions: {
      //以下文件不打包
      external: [
        "vue",
        "element-plus",
        "pinia",
        "axios",
        "vue-router",
        "echarts",
      ],
      plugins: [
        // 打包时自动将代码中的引入替换成cdn引入
        externalGlobals({
          vue: "Vue",
          "element-plus": "ElementPlus",
          pinia: "Pinia",
          axios: "axios",
          "vue-router": "VueRouter",
          echarts: "echarts",
        }),
      ],
      output: {
        manualChunks(id) {
          if (id.includes("node_modules")) {
            return id
              .toString()
              .split("node_modules/")[1]
              .split("/")[0]
              .toString();
          }
        },
      },
    },
  },
});

3.在 index.html 中进行 cdn 引入。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <script src="https://unpkg.com/vue@3.2.16/dist/vue.global.js"></script>
    <link
      rel="stylesheet"
      href="https://unpkg.com/element-plus@2.1.8/dist/index.css"
    />
    <script src="https://unpkg.com/element-plus@2.1.8/dist/index.full.js"></script>
    <script src="https://unpkg.com/pinia@2.0.36/dist/pinia.iife.js"></script>
    <script src="https://unpkg.com/axios@0.24.0/dist/axios.min.js"></script>
    <script src="https://unpkg.com/vue-router@4.1.6/dist/vue-router.global.js"></script>
    <script src="https://unpkg.com/echarts@5.3.0/dist/echarts.min.js"></script>
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no"
    />
    <title>Ache</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
  <style></style>
</html>

路由懒加载

在路由中通常会定义很多不同的页面,打包到同一个 js 文件中,文件将会异常的大,路由懒加载就是将各个模块分开打包,匹配到对应路由时再加载对应模块,减少加载时间。

也就是说,一开始进入页面时不需要一次性把资源都加载完,需要时再加载对应的资源。

以下介绍两种实现路由懒加载的方式。

1.ES6 的 import()

const routes = [
  {
    path: item.path,
    name: item.name,
    component: () => import(`/src/views/${item.view}.vue`),
  },
];

2.Vite 的 import.meta.glob()

const modules = import.meta.glob("/src/views/*.vue");

const routes = [
  {
    path: item.path,
    name: item.name,
    component: modules[`/src/views/${item.view}.vue`],
  },
];

组件异步加载

在 Vue 3.x 中,异步组件的导入需要使用辅助函数 defineAsyncComponent 来进行显式声明。

<script setup>
import { defineAsyncComponent } from "vue";
const child = defineAsyncComponent(() => import("/src/components/child.vue"));
</script>

<template>
  <child></child>
</template>