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 会失效。

总结

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

相关推荐
姑苏洛言1 小时前
编写产品需求文档:黄历日历小程序
前端·javascript·后端
知识分享小能手1 小时前
Vue3 学习教程,从入门到精通,使用 VSCode 开发 Vue3 的详细指南(3)
前端·javascript·vue.js·学习·前端框架·vue·vue3
姑苏洛言2 小时前
搭建一款结合传统黄历功能的日历小程序
前端·javascript·后端
你的人类朋友3 小时前
🤔什么时候用BFF架构?
前端·javascript·后端
知识分享小能手3 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3
一只小灿灿3 小时前
前端计算机视觉:使用 OpenCV.js 在浏览器中实现图像处理
前端·opencv·计算机视觉
前端小趴菜053 小时前
react状态管理库 - zustand
前端·react.js·前端框架
Jerry Lau4 小时前
go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
前端·golang·gin
我命由我123454 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js