前端打包性能优化-Vue3+vite 实践指南

前言

在现代 Web 开发中,前端性能优化至关重要。Vue 3 和 Vite 作为新一代的前端框架和构建工具,提供了许多性能优化的功能和实践方法。 本文结合实际开发做一些总结分享,帮助大家在以后的开发过程中能够实际运用到,希望能帮到大家。

性能指标

在开始优化之前,了解一些关键的性能指标非常重要:

  • 页面加载时间(Load Time) :从用户发出请求到页面完全加载所需的时间。
  • 首次内容绘制(First Contentful Paint, FCP) :浏览器渲染出第一个内容元素的时间点。
  • 最大内容绘制(Largest Contentful Paint, LCP) :页面主要内容加载完成的时间点。
  • 累计布局偏移(Cumulative Layout Shift, CLS) :页面视觉稳定性的衡量标准。
  • 交互延迟(Time to Interactive, TTI) :页面可完全交互的时间点。

总结一下就是:一个字,三个字 响应快

优化方向整体介绍

  1. 优化工具辅助工具,为什么我把它放在第一位因为在实际优化过程中它真的非常好用, rollup-plugin-visualizer是一个强大且直观的工具,用于帮助开发者在使用 Rollup 打包时,生成详细的模块依赖图谱。通过可视化的方式,你可以清晰地理解代码的打包结构和优化潜在点,从而更高效地管理和优化你的 JavaScript 应用程序。
  • 安装 npm install --save-dev rollup-plugin-visualizer 或者 yarn add --dev rollup-plugin-visualizer
  • 使用 rollup-plugin-visualizer 这里有一个坑在新版中是不支持默认导入,所有有些网上有些资料是直接默认导入的这样会报错 visualizer is not function,所以在实际使用中需要注意版本。
vite.config.ts 复制代码
import { defineConfig, PluginOption } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path';
import {visualizer} from 'rollup-plugin-visualizer'
// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: [
      {
        find: '@',
        replacement: resolve(__dirname, './src'),
      },
    ],
  },
  plugins: [
    vue(),
    visualizer({
      open: true //在默认用户代理中打开生成的文件
    }),
  ],
})
  • 可以清晰的知道每一个模块之间的依赖的关系,占比大小等等。

2.减少包的体积:按需使用第三方库提炼公共组件公共方法减少冗余代码压缩图片压缩字体文件等资源压缩

3.减少HTTP请求: 合并文件使用图像精灵(Sprite)技术使用字体图标代替图片

4.使用cdn:减轻源服务器的压力同域名的并发请求限制数量为一般6个

5.缓存策略:主要有HTTP缓存浏览器缓存

具体优化实施

1.router路由懒加载 +webpackchunkname

如果使用 router 的静态引入路由的话,打包后所有组件会打包成一个js和css 文件这样文件就非常大,影响页面加载,Vue Router 支持开箱即用的动态导入,这意味着你可以用动态导入代替静态导入。这样就可以把对应的组件打包成单独的文件,只会在路由被访问的时候才加载对应组件文件。

如果把所有的组件都打包成独立的文件,这样http请求数量又变多了,所以我们需要用到webpackChunkName将相同name的组件打包到一个文件中,因为我们是vite 项目 要想实现 webpackChunkName 还需要用到 vite-plugin-webpackchunkname 插件。插件如何使用我就不介绍了,主要看下路由文件怎么写的。

router/index.ts 复制代码
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

// import Home from '@/view/home/index.vue';
// import HomeInfo from '@/view/home/info.vue';
// import About from '@/view/about/index.vue';
// import AboutInfo from '@/view/about/info.vue';


const routes: RouteRecordRaw[] = [
    {
        path: '/',
        name: 'Layout',
        redirect: '/home',
        meta: {
            title: '根路由',
        },
        children: [
            {
                path: '/home',
                name: 'Home',
                component: () => import(/* webpackChunkName: "home" */'@/view/home/index.vue'),// Home,
                meta: {
                    title: '首页',
                },
            },
            {
                path: '/home/info',
                name: 'HomeInfo',
                component: () => import(/* webpackChunkName: "home" */'@/view/home/info.vue'),// HomeInfo,
                meta: {
                    title: '首页详情',
                },
            },
            {
                path: '/about',
                name: 'About',
                component: () => import(/* webpackChunkName: "about" */'@/view/about/index.vue'), //About,
                meta: {
                    title: '关于',
                },
            },
            {
                path: '/about/info',
                name: 'AboutInfo',
                component: () => import(/* webpackChunkName: "about" */'@/view/about/info.vue'), //AboutInfo,
                meta: {
                    title: '关于详情',
                },
            },

        ],
    }

];

const router = createRouter({
    history: createWebHistory(),
    routes,
});

export default router;
2.利用 vite build.rollupOptions.output manualChunks 合并拆分代码,利用 *FileNames 把文件分组方便查看维护 ,利用 terser 移除 打印 只需要安装 npm i terser 即可 配置参照下面内容。
vite.config.ts 复制代码
import { defineConfig, PluginOption } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path';
import {visualizer} from 'rollup-plugin-visualizer'
import { manualChunksPlugin } from 'vite-plugin-webpackchunkname'
// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: [
      {
        find: '@',
        replacement: resolve(__dirname, './src'),
      },
    ],
  },
  plugins: [
    vue(),
    visualizer({
      open: true //在默认用户代理中打开生成的文件
    }),
    manualChunksPlugin()
  ],
  build: {
    terserOptions: {
      compress: {
        //生产环境时移除console
        drop_console: true,
        drop_debugger: true,
      },
    },
    // 关闭文件计算
    reportCompressedSize: false,
    // 关闭生成map文件 可以达到缩小打包体积
    sourcemap: false, // 这个生产环境一定要关闭,不然打包的产物会很大
    // 压缩
    minify: false,
    cssCodeSplit: true,
    rollupOptions: {
      output: {
        // 入口文件名
        entryFileNames: `assets/js/[name]-[hash].js`,
        // 块文件名
        chunkFileNames: `assets/js/[name]-[hash].js`,
        // 资源文件名 css 图片等等
        assetFileNames: `assets/[ext]/[name]-[hash].[ext]`,
        // 大文件拆分
        manualChunks(id) {
          if (id.includes("node_modules")) {
             //把vue vue-router  @vueuse 等核心模块打包成一个文件
            if (id.includes('vue')) {
              return 'vue';
            }else{
              //最小化拆分包
              return id
                .toString()
                .split("node_modules/")[1]
                .split("/")[0]
                .toString();
            }
            
          }
        },
      },
    },
  },
})
3.使用图像精灵(Sprite)技术 这个技术在几年前很有必要,但是到了现在好像用到的就很少了,大部分都是用的字体图标库。

将多个小图标合并成一张大图,并使用 CSS 来显示其中的部分图像,减少 HTTP 请求。

首先,创建一个包含多个图标的 Sprite 图:将多个小图标合并成一张大图,并使用 CSS 来显示其中的部分图像,减少 HTTP 请求。

css 复制代码
/* styles/sprite.scss */
.icon {
  background-image: url('/path/to/sprite.png');
  background-repeat: no-repeat;
}

.icon-home {
  background-position: 0 0;
  width: 32px;
  height: 32px;
}

.icon-settings {
  background-position: -32px 0;
  width: 32px;
  height: 32px;
}
4.使用CDN 减少自己服务器负载,加快资源请求
  • 在 Vite 项目中,可以通过配置将某些依赖项从打包中排除,并改为从 CDN 加载。
vite.config.ts 复制代码
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [vue()],
  build: {
    rollupOptions: {
      external: ['vue'],
      output: {
        globals: {
          vue: 'Vue',
        }
      }
    }
  }
});
  • index.html 中引入 CDN 资源:
index.html 复制代码
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue + TS</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@3.4.29"></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
5. 静态压缩优化 npm i vite-plugin-compression -D 以及优化字体文件大小 字体压缩格式转换我直接推荐一个在线网站

安装插件 npm i vite-plugin-compression -D 这个需要后端配合 修改nginx 文件才有用

vite.config.ts 复制代码
import { defineConfig, PluginOption } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path';
import {visualizer} from 'rollup-plugin-visualizer'
import { manualChunksPlugin } from 'vite-plugin-webpackchunkname'
import { imagetools } from 'vite-imagetools';
// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: [
      {
        find: '@',
        replacement: resolve(__dirname, './src'),
      },
    ],
  },
  plugins: [
    vue(),
    visualizer({
      open: true //在默认用户代理中打开生成的文件
    }),
    manualChunksPlugin(),
    imagetools()
  ],
  build: {
    terserOptions: {
      compress: {
        //生产环境时移除console
        drop_console: true,
        drop_debugger: true,
      },
    },
    // 关闭文件计算
    reportCompressedSize: false,
    // 关闭生成map文件 可以达到缩小打包体积
    sourcemap: false, // 这个生产环境一定要关闭,不然打包的产物会很大
    // 压缩
    minify: false,
    cssCodeSplit: true,
    rollupOptions: {
      // 忽略打包vue文件
      external: ['vue'],
      output: {
        globals: {
          vue: 'Vue', //cdn 配置
        },
        // 入口文件名
        entryFileNames: `assets/js/[name]-[hash].js`,
        // 块文件名
        chunkFileNames: `assets/js/[name]-[hash].js`,
        // 资源文件名 css 图片等等
        assetFileNames: `assets/[ext]/[name]-[hash].[ext]`,
        // 大文件拆分
        manualChunks(id) {
          if (id.includes("node_modules")) {
             //把vue vue-router  @vueuse 等核心模块打包成一个文件
            if (id.includes('vue')) {
              return 'vue';
            }else{
              //最小化拆分包
              return id
                .toString()
                .split("node_modules/")[1]
                .split("/")[0]
                .toString();
            }
            
          }
        },
      },
    },
  },
})
6. 组件按需引入,一般参照官方文档配置即可

本项目用到了 按需引入 element-puls

首先你需要安装unplugin-vue-componentsunplugin-auto-import这两款插件

arduino 复制代码
npm install -D unplugin-vue-components unplugin-auto-import

完整 vite.config.ts 配置如下

vite.config.ts 复制代码
import { defineConfig, PluginOption } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'node:path';
import {visualizer} from 'rollup-plugin-visualizer'
import { manualChunksPlugin } from 'vite-plugin-webpackchunkname'
import viteCompression from 'vite-plugin-compression'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: [
      {
        find: '@',
        replacement: resolve(__dirname, './src'),
      },
    ],
  },
  plugins: [
    vue(),
    visualizer({
      open: true //在默认用户代理中打开生成的文件
    }),
    manualChunksPlugin(),
    // 静态资源压缩
    viteCompression({
      verbose: true, // 默认即可
      disable: false, // 开启压缩(不禁用),默认即可
      deleteOriginFile: false, // 删除源文件
      threshold: 5120, // 压缩前最小文件大小
      algorithm: 'gzip', // 压缩算法
      ext: '.gz' // 文件类型
    }),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
  build: {
    terserOptions: {
      compress: {
        //生产环境时移除console
        drop_console: true,
        drop_debugger: true,
      },
    },
    // 关闭文件计算
    reportCompressedSize: false,
    // 关闭生成map文件 可以达到缩小打包体积
    sourcemap: false, // 这个生产环境一定要关闭,不然打包的产物会很大
    // 压缩
    minify: false,
    cssCodeSplit: true,
    rollupOptions: {
      // 忽略打包vue文件
      external: ['vue'],
      output: {
        globals: {
          vue: 'Vue', //cdn 配置
        },
        // 入口文件名
        entryFileNames: `assets/js/[name]-[hash].js`,
        // 块文件名
        chunkFileNames: `assets/js/[name]-[hash].js`,
        // 资源文件名 css 图片等等
        assetFileNames: `assets/[ext]/[name]-[hash].[ext]`,
        // 大文件拆分
        manualChunks(id) {
          if (id.includes("node_modules")) {
             //把 vue-router  @vueuse 等核心模块打包成一个文件
            if (id.includes('vue-router') || id.includes('vueuse')) {
              return 'vue';
            }else{
              //最小化拆分包
              return id
                .toString()
                .split("node_modules/")[1]
                .split("/")[0]
                .toString();
            }
            
          }
        },
      },
    },
  },
})

总结

暂时想到的就这么多,希望通过本文知识点 结合 Vue 3 和 Vite 的性能优化策略,可以有效提升你的 Web 应用的性能。

最后放上本篇文章用到的项目应用demo 如果有疑问的可以把demo 下载下来自己去跑一跑 ,试着从头开始一步一步修改代码,多次打包看结果进行对比,这样可以加深理解。 如果这篇文章对你有所帮助,可以来波点赞收藏加关注。

相关推荐
一个处女座的程序猿O(∩_∩)O1 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink4 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者6 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-6 小时前
验证码机制
前端·后端
燃先生._.7 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖8 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235248 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240259 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar9 小时前
纯前端实现更新检测
开发语言·前端·javascript