vue3+vite项目性能优化

1-CDN加速

在项目中引入vite-plugin-cdn-import包即可,然后像下面这样使用

js 复制代码
import importToCDN from 'vite-plugin-cdn-import'; // CDN引入

export default function (isBuild) {
  // 开发环境不使用CDN
  if (!isBuild) return importToCDN({ modules: [] });
  return importToCDN({
    modules: [
      // {
      //   name: 'element-plus',
      //   var: 'ElementPlus',
      //   path: 'https://cdn.bootcdn.net/ajax/libs/element-plus/2.3.12/index.full.min.js',
      //   css: 'https://cdn.bootcdn.net/ajax/libs/element-plus/2.3.12/index.min.css'
      // },
    ],
  });
}

哪些方面可以使用cdn加速呢?一些比较大的图片或者不常改变的文件和SDK包等,比如element-plus或者vue还有echarts都可以放上去。(一般建议放公司cdn上,免费的容易挂)

2-按需加载

一些常用的ui库或者lodash库都可以实现按需加载,不必全局引入,比如使用element-ui,可以像下面这样使用:

js 复制代码
import AutoImport from 'unplugin-auto-import/vite'; // 自动引入api
import Components from 'unplugin-vue-components/vite'; // 自动引入组件
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; // ElementPlusResolver 去看文档 里面有介绍有那些ui库可以直接使用  直接看官网也有

export default function () {
  return [
    AutoImport({
      dts: false,
      imports: ['vue', 'pinia', 'vue-router', '@vueuse/core'],
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      dts: false,
      extensions: ['vue'],
      include: [/\.vue$/],
      dirs: ['src/components/'],
      resolvers: [ElementPlusResolver()],
    }),
  ];
}

使用时直接拿来就用,不必再写引入语句了。

3-Gzip压缩

简单理解就是把代码再次压缩成.gz包,从而大大减少代码体积,提高加载速度,如下使用:

js 复制代码
import viteCompression from 'vite-plugin-compression'; // 打包压缩

export default function () {
  return viteCompression({
    verbose: true, // 是否在控制台输出压缩结果
    disable: false, // 是否禁用
    // filter:()=>{}, // 过滤哪些资源不压缩 RegExp or (file: string) => boolean
    threshold: 1024 * 50, // 体积大于50KB才会被压缩,单位 b
    deleteOriginFile: false, // 压缩后是否删除源文件
    algorithm: 'gzip', // 压缩算法
    ext: '.gz', // 生成的压缩包后缀
  });
}

打包后可以看到会出多.gz后缀的包,体积非常小。但是这个得配合后端nginx添加配置项目,如下:

js 复制代码
//在nginx添加 
http { 
# 开启或者关闭gzip模块(on|off) 
gzip_static on; 
# gzip压缩比,1 压缩比最小处理速度最快,9 压缩比最大但处理最慢(传输快但比较消耗cpu)
gzip_comp_level 2; 
}

4-将svg图转雪碧图

雪碧图的作用还是挺多的,比如减少HTTP请求数量,减小图像文件大小和提高渲染性能。作用还是挺大的,而且用起来也方便。在项目中引入vite-plugin-svg-icons包即可(虽然这个包停止维护了,但是还是挺好用的)。 使用方式如下:

js 复制代码
import path from 'path';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';

// 将svg图标生成雪碧图
export default function (isBuild) {
  return createSvgIconsPlugin({
    customDomId: '__svg__icons__',
    iconDirs: [path.resolve(process.cwd(), 'src/assets/svgs')], // 指定需要缓存的图标文件夹
    symbolId: 'icon-[dir]-[name]', // 指定symbolId格式
    svgoOptions: isBuild,
  });
}

具体的使用方式可以在github上找到这个包,里面有详细的使用说明,vuereact都有。

5-图片压缩

使用的是这个包vite-plugin-imagemin,但是这个包国内比较难安装,可能需要科学上网才行。使用方式摘至另一个掘友的,代码如下:

js 复制代码
import viteImagemin from 'vite-plugin-imagemin'

plugin: [
    viteImagemin({
      gifsicle: {
        optimizationLevel: 7,
        interlaced: false
      },
      optipng: {
        optimizationLevel: 7
      },
      mozjpeg: {
        quality: 20
      },
      pngquant: {
        quality: [0.8, 0.9],
        speed: 4
      },
      svgo: {
        plugins: [
          {
            name: 'removeViewBox'
          },
          {
            name: 'removeEmptyAttrs',
            active: false
          }
        ]
      }
    })
]

6-图片懒加载

这个懒加载的话,有一些现有的很好用的包比如vue-lazyload或者lozad.js都可以。这里我使用的是自定义的指令来实现图片懒加载,更加贴合实际项目需求,代码如下:

js 复制代码
import { useIntersectionObserver } from '@vueuse/core';  // 判断当前元素是否在可视范围内
import defaultImg from '../assets/images/loading.svg'; // 加载时svg
import errorImg from '../assets/images/error-image.png'; // 加载错误时的图片
import noImg from '../assets/images/no-image.png'; // 没有图片时的默认图片
import api from '@/api';

const isImage = (url) => {
  return ['data:image','.jpg','.png','.jpeg','.gif','.bmp','.pic','.svg'].find((item) => url.includes(item));
};

const getImgUrl = async (item) => {
  if (!item) return '';
  if (item.filePath) return item.filePath;
  const url = await api.get(
    'api',
    { '备用字段': item.serverName }
  );
  return url;
};

// 获取图片地址  当src上没有传值时,则从绑定的对象上获取filePath,如果没有filePath,则从绑定的对象上获取备用字段,再通过接口来获取  三重保险
const getPreviewImg = async (src,{ value }) => {
  if (!src && !value) return '';
  const url = src || (await getImgUrl(value[0]));
  const str = url.toLowerCase();
  if (isImage(str)) return url;
  return noImg;
};

export default {
  name: 'lazyLoad',
  mounted(el,binding) {
    if (!['IMG'].includes(el.nodeName)) return;
    const { src } = el;
    el.src = defaultImg;
    const { stop } = useIntersectionObserver(el,([{ isIntersecting }]) => {
      if (isIntersecting) {
        el.onerror = () => {
          el.src = errorImg;
          el.onerror = null;
        };
        getPreviewImg(src,binding).then((url) => {
          el.src = url;
        });
        stop();
      }
    });
  },
};

7-页面缓存

js 复制代码
<template>
  <RouterView>
    <template #default="{ Component }">
      <div
        v-tloading="store.getPageLoading"
        loading-tip="加载中.."
      >
        <keep-alive :include="/.*content/">
          <component :is="Component" />
        </keep-alive>
      </div>
    </template>
  </RouterView>
</template>

这里有两个优化,第一个是当页面正在加载中时,会显示loading效果,在页面加载完成后loading效果去除。第二个是缓存页面中导出名字包含content字段的页面,同时需要时可以配合pinia实现数据持久化

8-数据缓存

比如常见的后台页面,主要由查询框和表格组成,查询框的下拉数据过多,此时可以将这部分数据缓存到本地使用,具体方法如下:

js 复制代码
// 在发起请求时判断是否有缓存,有则直接返回;没有则发起请求并将结果缓存
 reauest(config,options) {
  const cacheData = this.getCache(config);
  if (cacheData) return cacheData;
  const res = await this.requestxx(config);
  this.setCache(config, res);
  return res;
}

const responseMap = new Map(); // 返回结果缓存池
this.generateReqKey(config); // 用来将config转换成唯一的key

// 获取缓存数据
getCache(config) {
  // 2、没取到返回null
  const requestKey = this.generateReqKey(config);
  const data = responseMap.get(requestKey) || null;
  if (!data) return null;
  // 3、取到判断过期状态,如果过期清除缓存返回null,否则返回缓存数据
  if (data.time < Date.now()) {
    responseMap.delete(requestKey);
    return null;
  }
  return data.data;
}

// 设置缓存
setCache(config,data,cacheTime) {
  if (cacheTime) {
    const requestKey = this.generateReqKey(config);
    responseMap.set(requestKey,{ time: Date.now() + cacheTime,data });
  }
}

// 使用
export async function getDict(params) {
  const res = await api.get('xx',{...params },{ cacheTime: 1000 * 60 * 60 * 24 });
  return res;
}

注意,这里的缓存不是放在localStorage里的,所以当页面强制刷新时还是会重复调用。这里缓存的意义是在页面不刷新的情况下缓存,页面的查询框数据不变,其他部分改变。结合业务需求,也可以做成localStorage缓存并设置过期时间。

9-ssr

目前还不会,后续加上

10-DNS预解析

浏览器在向跨域的服务器发送请求时,首先会进行 DNS 解析,将服务器域名解析为对应的 IP 地址。我们通过 dns-prefetch 技术将这一过程提前,降低 DNS 解析的延迟时间,具体使用方式如下:

js 复制代码
<!-- href 为需要预解析的域名 -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com/"> 

一般情况下 dns-prefetch会与preconnect 搭配使用,前者用来解析 DNS,而后者用来会建立与服务器的连接,建立 TCP 通道及进行 TLS 握手,进一步降低请求延迟。使用方式如下所示:

js 复制代码
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
<link rel="dns-prefetch" href="https://fonts.gstatic.com/">

值得注意的是,对于 preconnect 的 link 标签一般需要加上 crorssorigin(跨域标识),否则对于一些字体资源 preconnect 会失效。

总结

有任何问题欢迎指正,加油!!!

相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax