基于vite对vue3项目打包优化经验 【前端性能优化】

2,922 阅读5分钟

性能工具

当项目打包完如何分析各个包的打包体积?

可以安装 rollup-plugin-visualizer插件(vite插件),它是一个打包体积分析插件,可以看到打包后的所有文件大小,可以用于判断配置vite优化前后包体积大小的变化

npm i rollup-plugin-visualizer -D
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
  plugins: [
     // 打包体积分析
    visualizer({
      open: true,
      filename: 'visualizer.html' //分析图生成的文件名
    }),
  ],
})

打包完成会生成一个html文件,就可以看到各个包的体积大小了

全部优化.png

vite打包优化过程

实现自动按需引入

利用unplugin-vue-components插件实现组件自动按需导入, 利用unplugin-auto-import插件实现自动按需引入

image.png

具体配置

npm install -D unplugin-vue-components unplugin-auto-import
// 按需自动引入导入element plus。
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

我项目是用在按需自动引入导入element plus库

export default defineConfig({
  plugins: [
    // 按需导入element plus组件
    AutoImport({
      resolvers: [ElementPlusResolver()]
    }),
    // 按需导入element plus 自定义主题。
    Components({
      resolvers: [ElementPlusResolver({ importStyle: 'sass' })]
    }),
  ],
})

配置打包文件分类输出

build: {
    rollupOptions: {
      output: {
        chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名称
        entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名称
        assetFileNames: '[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等
      }
    }
}

最小化拆分包

将需要分离的包单独的打包出来

  output: {
    // 最小化拆分包
    manualChunks(id) {
      if (id.includes('node_modules')) {
        return id.toString().split('node_modules/')[1].split('/')[0].toString();
      }
    }
  },

开启图片压缩

pnpm add vite-plugin-imagemin -D
// 图片压缩
import viteImagemin from 'vite-plugin-imagemin'
export default defineConfig({
  plugins: [
     // 图片资源压缩
    viteImagemin({
      gifsicle: {
        // gif图片压缩
        optimizationLevel: 3, // 选择1到3之间的优化级别
        interlaced: false // 隔行扫描gif进行渐进式渲染
      },
      optipng: { // png
        optimizationLevel: 7 // 选择0到7之间的优化级别
      },
      mozjpeg: {// jpeg
        quality: 20 // 压缩质量,范围从0(最差)到100(最佳)。
      },
      pngquant: {// png
        quality: [0.8, 0.9], // Min和max是介于0(最差)到1(最佳)之间的数字,类似于JPEG。达到或超过最高质量所需的最少量的颜色。如果转换导致质量低于最低质量,图像将不会被保存。
        speed: 4 // 压缩速度,1(强力)到11(最快)
      },
      svgo: {
        plugins: [
          // svg压缩
          {
            name: 'removeViewBox'
          },
          {
            name: 'removeEmptyAttrs',
            active: false
          }
        ]
      }
    }),
  ],
})

开启压缩后图片体积大小减少60%以上

压缩后的图片.png

文件压缩

问:开启Gzip压缩能压缩哪些类型的文件?

1、对文本进行压缩

支持对文本进行压缩,这里的文本包括 HTML、CSS、JS文件,都会进行不一定程度的压缩。

2、不支持对非文本类型压缩

像咱们常见的图片(JPEG、PNG、GIF)都属于非文本类型,gzip 都不支持压缩哦。

pnpm add vite-plugin-compression -D
//Gzip文件压缩
import viteCompression from 'vite-plugin-compression'
export default defineConfig({
  plugins: [
     //开启Gzip压缩
    viteCompression({
      verbose: true, // 是否在控制台中输出压缩结果
      disable: false,
      threshold: 1024, // 如果体积大于阈值,将被压缩,单位为b,体积过小时请不要压缩,以免适得其反
      algorithm: 'gzip', // 压缩算法,可选['gzip'' brotliccompress ''deflate ''deflateRaw']
      ext: '.gz',
      deleteOriginFile: true // 源文件压缩后是否删除(我为了看压缩后的效果,先选择了true)
    })
  ],
})

开启CDN加速

pnpm add rollup-plugin-external-globals -D
const globals = externalGlobals({
  lodash: 'lodash',
  jspdf: 'jspdf',
  html2canvas: 'html2canvas'
})

export default defineConfig({
  build: {
    rollupOptions: {
      //打包时不引入外部模块,使用cdn引入
      external: ['lodash', 'jspdf', 'html2canvas'],
      plugins: [globals]
    },
  }
})

在index.html文件的head标签内引入对应库的CDN

image.png

具体的CDN链接根据自己需要去官网或是CDN网站查询,cdn网站:cdnjs.com/

terser 压缩和去除console+debugger

npm i terser -D

export default defineConfig({
   build: {
    minify: 'terser',
    // 清除所有console和debugger
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  }
})

最后,项目打包后的体积大小从4.52MB变小为2.86MB

优化前

优化前.png

优化后

全部优化.png

其它还未应用的优化

预渲染

进行SEO优化

npm i vite-plugin-prerender -D
// 预渲染
import vitePrerender from 'vite-plugin-prerender'
export default defineConfig({
  plugins: [
    // 预渲染
    vitePrerender({
      staticDir: path.join(__dirname, 'dist'),
      routes: ['/', '/design', '/index']
    }),
  ],
})

打包完成后,会根据配置的路由地址,在dist文件中生成对应的index.html文件

SVG图标优化

svg组件封装优化是我在其它项目使用的优化

在开发项目的时候会用到svg矢量图,而且我们使用SVG以后,页面上加载的不再是图片资源, 这对页面性能来说是个很大的提升,而且SVG文件比img体积大小要小很多

安装SVG依赖插件

pnpm install vite-plugin-svg-icons -D

vite.config.ts中配置插件

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default () => {
  return {
    plugins: [
      createSvgIconsPlugin({
        // Specify the icon folder to be cached
        iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
        // Specify symbolId format
        symbolId: 'icon-[dir]-[name]',
      }),
    ],
  }
}

入口文件导入

import 'virtual:svg-icons-register'

svg组件封装

<template>
  <div>
    <svg :style="{ width: width, height: height }">
      <use :xlink:href="prefix + name" :fill="color"></use>
    </svg>
  </div>
</template>

<script setup lang="ts">
defineProps({
  //xlink:href属性值的前缀
  prefix: {
    type: String,
    default: '#icon-'
  },
  //svg矢量图的名字
  name: String,
  //svg图标的颜色
  color: {
    type: String,
    default: ""
  },
  //svg宽度
  width: {
    type: String,
    default: '16px'
  },
  //svg高度
  height: {
    type: String,
    default: '16px'
  }

})
</script>
<style scoped></style>

按需加载第三方包的体积

比如loadsh,项目中可能只用到了cloneDeep 、throttle这几个函数,但是由于lodash是common.js版本不支持按需引入,整个包都会被打包进来,影响性能

lodash-es 是 lodash 的 es modules 版本 ,具备 ES6 模块化的版本,可以按需导入,使用loadsh-es代替lodash,按需引入,减少打包体积

import _ from 'lodash-es'; // 将会把整个lodash的库引入到项目
import { cloneDeep } from 'lodash-es'; // 将只会把cloneDeep引入到项目

自动重启 vite 服务

使用 vite-plugin-restart 插件,监听 vite.config.js.env.development 等配置文件修改直接生效,不需要反复重启 Vite

npm i vite-plugin-restart -D

import ViteRestart from 'vite-plugin-restart'
export default {
  plugins: [
    ViteRestart({
      restart: [
        'my.config.[jt]s'
      ]
    })
  ],
}